Replace SVGFilter using SVGIO

Target of this change is to get rid of the SVGFilter
in current LO which is based on the standard-ODF importer
using it's functionality by passing in/handing over
temporary created XML-Stream-Data.

First step is to alternatively import the given SVG
file to a newly created Document and to strip the
existing Filter-Code.

Adding the first prototype of the changed import. It
is capable of importing the given SVG to the created
Draw/Impress document. It adds the SVG which gets imported
by SVGIO as GraphicObject. It adapts sizes and positions of
Page and GraphicObject to have a smooth import.

Adding stripping of SVGFilter and used ressources. Done
as deep as possible, hopefully all places found.

Adapted now to create an Impress document. Also added needed
adaptions to PageSize(s), including layout and PresObj stuff
to make all MasterPages/LayoutPages work correctly.

Added reaction on empty SVG. This is needed since the
PageSize is adapted to the Graphic. With empty Graphic
a Size(0,0) results and this goes wrong.

Change-Id: Ia364a5783bee7dadcbe91e700efbabc121cf98f9
Reviewed-on: https://gerrit.libreoffice.org/54096
Tested-by: Jenkins <ci@libreoffice.org>
Reviewed-by: Armin Le Grand <Armin.Le.Grand@cib.de>
diff --git a/Repository.mk b/Repository.mk
index 4cd3f10..7a019e2 100644
--- a/Repository.mk
+++ b/Repository.mk
@@ -53,7 +53,6 @@ $(eval $(call gb_Helper_register_executables,NONE, \
	regsvrex \
	saxparser \
	sp2bv \
	svg2odf \
	svidl \
	$(if $(ENABLE_ONLINE_UPDATE_MAR),\
		test_updater_dialog \
diff --git a/compilerplugins/clang/store/constantfunction.cxx b/compilerplugins/clang/store/constantfunction.cxx
index 19c28df..f3dd4f8 100644
--- a/compilerplugins/clang/store/constantfunction.cxx
+++ b/compilerplugins/clang/store/constantfunction.cxx
@@ -109,10 +109,6 @@ bool ConstantFunction::VisitFunctionDecl(const FunctionDecl * pFunctionDecl) {
    if (aFileName == SRCDIR "/extensions/source/plugin/unx/npnapi.cxx") {
        return true;
    }
    // template magic
    if (aFileName == SRCDIR "/filter/source/svg/svgreader.cxx") {
        return true;
    }
    // vcl/unx/gtk3 re-using vcl/unx/gtk:
    if (aFileName.find("/../../gtk/") != std::string::npos) {
        return true;
diff --git a/filter/Executable_svg2odf.mk b/filter/Executable_svg2odf.mk
deleted file mode 100644
index d638086..0000000
--- a/filter/Executable_svg2odf.mk
+++ /dev/null
@@ -1,46 +0,0 @@
# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
#
# This file is part of the LibreOffice project.
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
#

$(eval $(call gb_Executable_Executable,svg2odf))

$(eval $(call gb_Executable_set_targettype_gui,svg2odf,YES))

$(eval $(call gb_Executable_use_external,svg2odf,boost_headers))

$(eval $(call gb_Executable_use_sdk_api,svg2odf))

$(eval $(call gb_Executable_set_include,svg2odf,\
    $$(INCLUDE) \
    -I$(SRCDIR)/filter/inc \
))

$(eval $(call gb_Executable_use_libraries,svg2odf,\
    svgfilter \
	svxcore \
	editeng \
	xo \
	svt \
	vcl \
	svl \
	utl \
	tl \
	sax \
	comphelper \
	basegfx \
	cppuhelper \
	cppu \
	sal \
))

$(eval $(call gb_Executable_add_exception_objects,svg2odf,\
    filter/source/svg/test/svg2odf \
    filter/source/svg/test/odfserializer \
))

# vim: set ts=4 sw=4 et:
diff --git a/filter/Library_svgfilter.mk b/filter/Library_svgfilter.mk
index 9708b8c..bdd917a 100644
--- a/filter/Library_svgfilter.mk
+++ b/filter/Library_svgfilter.mk
@@ -68,13 +68,7 @@ $(eval $(call gb_Library_use_externals,svgfilter,\
))

$(eval $(call gb_Library_add_exception_objects,svgfilter,\
	filter/source/svg/b2dellipse \
	filter/source/svg/parserfragments \
	filter/source/svg/svgfilter \
	filter/source/svg/svgimport \
	filter/source/svg/svgreader \
	filter/source/svg/tokenmap \
	filter/source/svg/units \
	filter/source/svg/svgexport \
	filter/source/svg/svgfontexport \
	filter/source/svg/svgwriter \
diff --git a/filter/Module_filter.mk b/filter/Module_filter.mk
index 08aa0f2..2d380fb 100644
--- a/filter/Module_filter.mk
+++ b/filter/Module_filter.mk
@@ -47,12 +47,6 @@ $(eval $(call gb_Module_add_l10n_targets,filter,\
	AllLangMoTarget_flt \
))

ifneq (,$(filter DESKTOP,$(BUILD_TYPE)))
$(eval $(call gb_Module_add_targets,filter,\
	Executable_svg2odf \
))
endif

$(eval $(call gb_Module_add_check_targets,filter,\
    CppunitTest_filter_xslt \
    CppunitTest_filter_priority \
diff --git a/filter/inc/parserfragments.hxx b/filter/inc/parserfragments.hxx
deleted file mode 100644
index f8b4560..0000000
--- a/filter/inc/parserfragments.hxx
+++ /dev/null
@@ -1,69 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */
#ifndef INCLUDED_FILTER_INC_PARSERFRAGMENTS_HXX
#define INCLUDED_FILTER_INC_PARSERFRAGMENTS_HXX

#include <sal/config.h>
#include <vector>
#include <utility>
#include <rtl/ustring.hxx>

namespace basegfx
{
    class B2DHomMatrix;
    class B2DRange;
}
namespace svgi
{
    struct ARGBColor;

    /// Parse given string for one of the SVG color grammars
    bool parseColor( const char* sColor, ARGBColor& rColor );
    bool parseOpacity( const char* sOpacity, ARGBColor& rColor );

    /// Parse given string for one of the SVG transformation grammars
    bool parseTransform( const char* sTransform, basegfx::B2DHomMatrix& rTransform );

    /// Parse given string for the viewBox attribute
    bool parseViewBox( const char* sViewbox, basegfx::B2DRange& rRect );

    /// Parse given string for a list of double values, comma-delimited
    bool parseDashArray( const char* sDashArray, std::vector<double>& rOutputVector );

    /** Parse paint uri

        @param o_rPaintUri
        Start and end ptr for uri substring (within
        [sPaintUri,sPaintUri+strlen(sPaintUri)]

        @param io_rColor
        The optional paint color to use. if o_rPaintUri is empty,
        parser sets io_rColor.second to false for color="None", to
        true and keeps current io_rColor.first entry for
        "currentColor", and to true and sets io_rColor.first to parsed
        color otherwise.

        @param sPaintUri
        String to parse. Permitted to contain the optional paint
        stuff, like fallback color.

        @return true, if a paint uri was successfully parsed.
     */
    bool parsePaintUri( std::pair<const char*,const char*>& o_rPaintUri,
                        std::pair<ARGBColor,bool>&          io_rColor,
                        const char*                         sPaintUri );

    /// Parse given string for the xlink attribute
    bool parseXlinkHref( const char* xlink, OUString& data );

} // namespace svgi

#endif

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/filter/inc/svgreader.hxx b/filter/inc/svgreader.hxx
deleted file mode 100644
index 3149e17..0000000
--- a/filter/inc/svgreader.hxx
+++ /dev/null
@@ -1,47 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */
#ifndef INCLUDED_FILTER_INC_SVGREADER_HXX
#define INCLUDED_FILTER_INC_SVGREADER_HXX

#include <filter/dllapi.h>

#include <com/sun/star/uno/XComponentContext.hpp>
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#include <com/sun/star/xml/sax/XDocumentHandler.hpp>
#include <com/sun/star/io/XInputStream.hpp>
#include <xmloff/xmlimp.hxx>

namespace svgi
{

class SVGReader
{
private:
    const css::uno::Reference< css::uno::XComponentContext >        m_xContext;
    const css::uno::Reference< css::io::XInputStream >              m_xInputStream;
    const css::uno::Reference< css::xml::sax::XDocumentHandler >    m_xDocumentHandler;

    SVGReader( const css::uno::Reference<css::uno::XComponentContext>&  xContext,
               const css::uno::Reference< css::io::XInputStream >&               xInputStream,
               const css::uno::Reference< css::xml::sax::XDocumentHandler >& xDocumentHandler,
               SvXMLImport *pFastHandler);

public:
    FILTER_DLLPUBLIC SVGReader( const css::uno::Reference<css::uno::XComponentContext>&  xContext,
               const css::uno::Reference< css::io::XInputStream >&               xInputStream,
               const css::uno::Reference< css::xml::sax::XDocumentHandler >& xDocumentHandler );

    FILTER_DLLPUBLIC bool parseAndConvert();
};

} // namespace svgi

#endif

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/filter/source/config/fragments/filters/SVG___Scalable_Vector_Graphics.xcu b/filter/source/config/fragments/filters/SVG___Scalable_Vector_Graphics.xcu
index b6d816e..c5cae01 100644
--- a/filter/source/config/fragments/filters/SVG___Scalable_Vector_Graphics.xcu
+++ b/filter/source/config/fragments/filters/SVG___Scalable_Vector_Graphics.xcu
@@ -26,5 +26,5 @@
        <prop oor:name="FileFormatVersion"><value>0</value></prop>
        <prop oor:name="Type"><value>svg_Scalable_Vector_Graphics</value></prop>
        <prop oor:name="TemplateName"/>
        <prop oor:name="DocumentService"><value>com.sun.star.drawing.DrawingDocument</value></prop>
        <prop oor:name="DocumentService"><value>com.sun.star.presentation.PresentationDocument</value></prop>
    </node>
diff --git a/filter/source/svg/b2dellipse.cxx b/filter/source/svg/b2dellipse.cxx
deleted file mode 100644
index b476109..0000000
--- a/filter/source/svg/b2dellipse.cxx
+++ /dev/null
@@ -1,27 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */
#include "b2dellipse.hxx"

#include <basegfx/point/b2dpoint.hxx>
#include <basegfx/matrix/b2dhommatrix.hxx>

namespace basegfx
{
    B2DEllipse::B2DEllipse(const basegfx::B2DPoint& rCenter, const basegfx::B2DTuple& rRadius)
    :   maCenter(rCenter), maRadius(rRadius)
    {
    }

    B2DEllipse::~B2DEllipse()
    {
    }

} // end of namespace basegfx

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/filter/source/svg/b2dellipse.hxx b/filter/source/svg/b2dellipse.hxx
deleted file mode 100644
index af97794..0000000
--- a/filter/source/svg/b2dellipse.hxx
+++ /dev/null
@@ -1,51 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */
#ifndef INCLUDED_FILTER_SOURCE_SVG_B2DELLIPSE_HXX
#define INCLUDED_FILTER_SOURCE_SVG_B2DELLIPSE_HXX

#include <sal/types.h>

#include <o3tl/cow_wrapper.hxx>

#include <basegfx/point/b2dpoint.hxx>

#include <basegfx/tuple/b2dtuple.hxx>

#include <basegfx/vector/b2enums.hxx>

namespace basegfx
{
    class B2DPoint;
} // end of namespace basegfx

namespace basegfx
{
    class B2DEllipse
    {
    private:
        const basegfx::B2DPoint maCenter;
        const basegfx::B2DTuple maRadius;

    public:
        B2DEllipse(const B2DEllipse& rEllipse);
        B2DEllipse(const basegfx::B2DPoint& rCenter, const basegfx::B2DTuple& rRadius);
        ~B2DEllipse();

        // assignment operator
        B2DEllipse& operator=(const B2DEllipse& rEllipse);

        // Coordinate interface
        const basegfx::B2DPoint& getB2DEllipseCenter() const { return maCenter; }
        const basegfx::B2DTuple& getB2DEllipseRadius() const { return maRadius; }
    };
} // end of namespace basegfx

#endif // INCLUDED_FILTER_SOURCE_SVG_B2DELLIPSE_HXX

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/filter/source/svg/parserfragments.cxx b/filter/source/svg/parserfragments.cxx
deleted file mode 100644
index 02c3708..0000000
--- a/filter/source/svg/parserfragments.cxx
+++ /dev/null
@@ -1,587 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include <parserfragments.hxx>
#include "spirit_supplements.hxx"
#include <gfxtypes.hxx>

#include <basegfx/utils/canvastools.hxx>
#include <com/sun/star/geometry/AffineMatrix2D.hpp>

#include <limits.h>
#include <boost/bind.hpp>
#include <boost/spirit/include/classic.hpp>
#include <boost/spirit/include/classic_while.hpp>
#include <numeric>
#include <algorithm>

#include "units.hxx"
#include "tokenmap.hxx"
#include <sal/log.hxx>

using namespace ::com::sun::star;

namespace svgi
{

inline sal_uInt8 hex2int( char val )
{
    return val <= '9' ? val-'0' : (val < 'a' ? val+10-'A' : val+10-'a');
}

void setFourBitColor( double& rChannel, char nChar )
{
    const sal_uInt8 nVal(hex2int(nChar));
    rChannel = (nVal*16+nVal)/255.0;
}

void setEightBitColor( double& rChannel, const char* pStart )
{
    const sal_uInt8 nVal0(hex2int(pStart[0]));
    const sal_uInt8 nVal1(hex2int(pStart[1]));
    rChannel = (nVal0*16+nVal1)/255.0;
}

void setIntColor( double& rChannel, sal_uInt8 nVal )
{
    rChannel = nVal/255.0;
}

void setPercentColor( double& rChannel, double nVal )
{
    rChannel = nVal/100.0;
}

void calcRotation(std::vector<geometry::AffineMatrix2D>& rTransforms,
                  geometry::AffineMatrix2D&              rCurrTransform,
                  double                                 fRotationAngle)
{
    ::basegfx::B2DHomMatrix aCurr;
    aCurr.translate(-rCurrTransform.m02,-rCurrTransform.m12);
    aCurr.rotate(fRotationAngle*M_PI/180);
    aCurr.translate(rCurrTransform.m02,rCurrTransform.m12);

    rTransforms.push_back(
        basegfx::unotools::affineMatrixFromHomMatrix(
            rCurrTransform,
            aCurr));
}

void calcSkewX(std::vector<geometry::AffineMatrix2D>& rTransforms,
               double                                 fSkewAngle)
{
    geometry::AffineMatrix2D aMat(1.0,tan(fSkewAngle*M_PI/180),0.0,
                                  0.0,1.0,0.0);
    rTransforms.push_back(aMat);
}

void calcSkewY(std::vector<geometry::AffineMatrix2D>& rTransforms,
               double                                 fSkewAngle)
{
    geometry::AffineMatrix2D aMat(1.0,0.0,0.0,
                                  tan(fSkewAngle*M_PI/180),1.0,0.0);
    rTransforms.push_back(aMat);
}

void assign_twice(double& r_oVal1, double& r_oVal2, const double& rInVal )
{
    r_oVal1 = r_oVal2 = rInVal;
}

geometry::AffineMatrix2D multiplyMatrix( const geometry::AffineMatrix2D& rLHS,
                                         const geometry::AffineMatrix2D& rRHS )
{
    basegfx::B2DHomMatrix aLHS;
    basegfx::B2DHomMatrix aRHS;

    basegfx::unotools::homMatrixFromAffineMatrix(aLHS,rLHS);
    basegfx::unotools::homMatrixFromAffineMatrix(aRHS,rRHS);

    aRHS*=aLHS;

    geometry::AffineMatrix2D aRet;
    return basegfx::unotools::affineMatrixFromHomMatrix(aRet,aRHS);
}

namespace
{
    struct ColorGrammar : public ::boost::spirit::classic::grammar< ColorGrammar >
    {
    public:
        ARGBColor& m_rColor;
        explicit ColorGrammar( ARGBColor& rColor ) : m_rColor(rColor) {}
        template< typename ScannerT >
        struct definition
        {
            ::boost::spirit::classic::rule< ScannerT > colorExpression;
            explicit definition( const ColorGrammar& self )
            {
                using namespace ::boost::spirit::classic;

                auto lambdaSetEightBitColorR = [&self](const char* pStart, const char* /*nChar*/){ setEightBitColor(self.m_rColor.r, pStart); };
                auto lambdaSetEightBitColorG = [&self](const char* pStart, const char* /*nChar*/){ setEightBitColor(self.m_rColor.g, pStart); };
                auto lambdaSetEightBitColorB = [&self](const char* pStart, const char* /*nChar*/){ setEightBitColor(self.m_rColor.b, pStart); };
                auto lambdaSetFourBitColorR = [&self](char nChar){ setFourBitColor(self.m_rColor.r, nChar); };
                auto lambdaSetFourBitColorG = [&self](char nChar){ setFourBitColor(self.m_rColor.g, nChar); };
                auto lambdaSetFourBitColorB = [&self](char nChar){ setFourBitColor(self.m_rColor.b, nChar); };
                auto lambdaSetIntColorR = [&self](sal_uInt8 nVal){ setIntColor(self.m_rColor.r, nVal); };
                auto lambdaSetIntColorG = [&self](sal_uInt8 nVal){ setIntColor(self.m_rColor.g, nVal); };
                auto lambdaSetIntColorB = [&self](sal_uInt8 nVal){ setIntColor(self.m_rColor.b, nVal); };
                auto lambdaSetPercentColorR = [&self](double nVal){ setPercentColor(self.m_rColor.r, nVal); };
                auto lambdaSetPercentColorG = [&self](double nVal){ setPercentColor(self.m_rColor.g, nVal); };
                auto lambdaSetPercentColorB = [&self](double nVal){ setPercentColor(self.m_rColor.b, nVal); };
                int_parser<sal_uInt8,10,1,3> byte_p;
                colorExpression =
                    (
                        // the #rrggbb form
                        ('#' >> (xdigit_p >> xdigit_p)[ lambdaSetEightBitColorR ]
                             >> (xdigit_p >> xdigit_p)[ lambdaSetEightBitColorG ]
                             >> (xdigit_p >> xdigit_p)[ lambdaSetEightBitColorB ]   )
                        |
                        // the #rgb form
                        ('#' >> xdigit_p[ lambdaSetFourBitColorR ]
                             >> xdigit_p[ lambdaSetFourBitColorG ]
                             >> xdigit_p[ lambdaSetFourBitColorB ]   )
                        |
                        // rgb() form
                        (str_p("rgb")
                            >> '(' >>
                            (
                                // rgb(int,int,int)
                                (byte_p[ lambdaSetIntColorR ] >> ',' >>
                                 byte_p[ lambdaSetIntColorG ] >> ',' >>
                                 byte_p[ lambdaSetIntColorB ] )
                             |
                                // rgb(double,double,double)
                                (real_p[assign_a(self.m_rColor.r)] >> ',' >>
                                 real_p[assign_a(self.m_rColor.g)] >> ',' >>
                                 real_p[assign_a(self.m_rColor.b)])
                             |
                                // rgb(percent,percent,percent)
                                (real_p[ lambdaSetPercentColorR ] >> "%," >>
                                 real_p[ lambdaSetPercentColorG ] >> "%," >>
                                 real_p[ lambdaSetPercentColorB ] >> "%")
                             )
                         >> ')')
                     );
            }
            ::boost::spirit::classic::rule<ScannerT> const& start() const { return colorExpression; }
        };
    };
}

bool parseColor( const char* sColor, ARGBColor& rColor  )
{
    using namespace ::boost::spirit::classic;

    if( parse(sColor,
              ColorGrammar(rColor) >> end_p,
              space_p).full )
    {
        // free-form color found & parsed
        return true;
    }

    // no free-form color - maybe a color name?
    // trim white space before
    while( *sColor &&
           (*sColor==' ' || *sColor=='\t' || *sColor=='\r' || *sColor=='\n') )
        ++sColor;
    // trim white space after
    int nLen=strlen(sColor)-1;
    while( nLen &&
           (sColor[nLen]==' ' || sColor[nLen]=='\t' || sColor[nLen]=='\r' || sColor[nLen]=='\n') )
        --nLen;
    switch (getTokenId(sColor, nLen+1))
    {
        case XML_ALICEBLUE: rColor = ARGBColor(240,248,255); return true;
        case XML_ANTIQUEWHITE: rColor = ARGBColor(250,235,215); return true;
        case XML_AQUA: rColor = ARGBColor(0,255,255); return true;
        case XML_AQUAMARINE: rColor = ARGBColor(127,255,212); return true;
        case XML_AZURE: rColor = ARGBColor(240,255,255); return true;
        case XML_BEIGE: rColor = ARGBColor(245,245,220); return true;
        case XML_BISQUE: rColor = ARGBColor(255,228,196); return true;
        case XML_BLACK: rColor = ARGBColor(0,0,0); return true;
        case XML_BLANCHEDALMOND: rColor = ARGBColor(255,235,205); return true;
        case XML_BLUE: rColor = ARGBColor(0,0,255); return true;
        case XML_BLUEVIOLET: rColor = ARGBColor(138,43,226); return true;
        case XML_BROWN: rColor = ARGBColor(165,42,42); return true;
        case XML_BURLYWOOD: rColor = ARGBColor(222,184,135); return true;
        case XML_CADETBLUE: rColor = ARGBColor(95,158,160); return true;
        case XML_CHARTREUSE: rColor = ARGBColor(127,255,0); return true;
        case XML_CHOCOLATE: rColor = ARGBColor(210,105,30); return true;
        case XML_CORAL: rColor = ARGBColor(255,127,80); return true;
        case XML_CORNFLOWERBLUE: rColor = ARGBColor(100,149,237); return true;
        case XML_CORNSILK: rColor = ARGBColor(255,248,220); return true;
        case XML_CRIMSON: rColor = ARGBColor(220,20,60); return true;
        case XML_CYAN: rColor = ARGBColor(0,255,255); return true;
        case XML_DARKBLUE: rColor = ARGBColor(0,0,139); return true;
        case XML_DARKCYAN: rColor = ARGBColor(0,139,139); return true;
        case XML_DARKGOLDENROD: rColor = ARGBColor(184,134,11); return true;
        case XML_DARKGRAY: rColor = ARGBColor(169,169,169); return true;
        case XML_DARKGREEN: rColor = ARGBColor(0,100,0); return true;
        case XML_DARKGREY: rColor = ARGBColor(169,169,169); return true;
        case XML_DARKKHAKI: rColor = ARGBColor(189,183,107); return true;
        case XML_DARKMAGENTA: rColor = ARGBColor(139,0,139); return true;
        case XML_DARKOLIVEGREEN: rColor = ARGBColor(85,107,47); return true;
        case XML_DARKORANGE: rColor = ARGBColor(255,140,0); return true;
        case XML_DARKORCHID: rColor = ARGBColor(153,50,204); return true;
        case XML_DARKRED: rColor = ARGBColor(139,0,0); return true;
        case XML_DARKSALMON: rColor = ARGBColor(233,150,122); return true;
        case XML_DARKSEAGREEN: rColor = ARGBColor(143,188,143); return true;
        case XML_DARKSLATEBLUE: rColor = ARGBColor(72,61,139); return true;
        case XML_DARKSLATEGRAY: rColor = ARGBColor(47,79,79); return true;
        case XML_DARKSLATEGREY: rColor = ARGBColor(47,79,79); return true;
        case XML_DARKTURQUOISE: rColor = ARGBColor(0,206,209); return true;
        case XML_DARKVIOLET: rColor = ARGBColor(148,0,211); return true;
        case XML_DEEPPINK: rColor = ARGBColor(255,20,147); return true;
        case XML_DEEPSKYBLUE: rColor = ARGBColor(0,191,255); return true;
        case XML_DIMGRAY: rColor = ARGBColor(105,105,105); return true;
        case XML_DIMGREY: rColor = ARGBColor(105,105,105); return true;
        case XML_DODGERBLUE: rColor = ARGBColor(30,144,255); return true;
        case XML_FIREBRICK: rColor = ARGBColor(178,34,34); return true;
        case XML_FLORALWHITE: rColor = ARGBColor(255,250,240); return true;
        case XML_FORESTGREEN: rColor = ARGBColor(34,139,34); return true;
        case XML_FUCHSIA: rColor = ARGBColor(255,0,255); return true;
        case XML_GAINSBORO: rColor = ARGBColor(220,220,220); return true;
        case XML_GHOSTWHITE: rColor = ARGBColor(248,248,255); return true;
        case XML_GOLD: rColor = ARGBColor(255,215,0); return true;
        case XML_GOLDENROD: rColor = ARGBColor(218,165,32); return true;
        case XML_GRAY: rColor = ARGBColor(128,128,128); return true;
        case XML_GREY: rColor = ARGBColor(128,128,128); return true;
        case XML_GREEN: rColor = ARGBColor(0,128,0); return true;
        case XML_GREENYELLOW: rColor = ARGBColor(173,255,47); return true;
        case XML_HONEYDEW: rColor = ARGBColor(240,255,240); return true;
        case XML_HOTPINK: rColor = ARGBColor(255,105,180); return true;
        case XML_INDIANRED: rColor = ARGBColor(205,92,92); return true;
        case XML_INDIGO: rColor = ARGBColor(75,0,130); return true;
        case XML_IVORY: rColor = ARGBColor(255,255,240); return true;
        case XML_KHAKI: rColor = ARGBColor(240,230,140); return true;
        case XML_LAVENDER: rColor = ARGBColor(230,230,250); return true;
        case XML_LAVENDERBLUSH: rColor = ARGBColor(255,240,245); return true;
        case XML_LAWNGREEN: rColor = ARGBColor(124,252,0); return true;
        case XML_LEMONCHIFFON: rColor = ARGBColor(255,250,205); return true;
        case XML_LIGHTBLUE: rColor = ARGBColor(173,216,230); return true;
        case XML_LIGHTCORAL: rColor = ARGBColor(240,128,128); return true;
        case XML_LIGHTCYAN: rColor = ARGBColor(224,255,255); return true;
        case XML_LIGHTGOLDENRODYELLOW: rColor = ARGBColor(250,250,210); return true;
        case XML_LIGHTGRAY: rColor = ARGBColor(211,211,211); return true;
        case XML_LIGHTGREEN: rColor = ARGBColor(144,238,144); return true;
        case XML_LIGHTGREY: rColor = ARGBColor(211,211,211); return true;
        case XML_LIGHTPINK: rColor = ARGBColor(255,182,193); return true;
        case XML_LIGHTSALMON: rColor = ARGBColor(255,160,122); return true;
        case XML_LIGHTSEAGREEN: rColor = ARGBColor(32,178,170); return true;
        case XML_LIGHTSKYBLUE: rColor = ARGBColor(135,206,250); return true;
        case XML_LIGHTSLATEGRAY: rColor = ARGBColor(119,136,153); return true;
        case XML_LIGHTSLATEGREY: rColor = ARGBColor(119,136,153); return true;
        case XML_LIGHTSTEELBLUE: rColor = ARGBColor(176,196,222); return true;
        case XML_LIGHTYELLOW: rColor = ARGBColor(255,255,224); return true;
        case XML_LIME: rColor = ARGBColor(0,255,0); return true;
        case XML_LIMEGREEN: rColor = ARGBColor(50,205,50); return true;
        case XML_LINEN: rColor = ARGBColor(250,240,230); return true;
        case XML_MAGENTA: rColor = ARGBColor(255,0,255); return true;
        case XML_MAROON: rColor = ARGBColor(128,0,0); return true;
        case XML_MEDIUMAQUAMARINE: rColor = ARGBColor(102,205,170); return true;
        case XML_MEDIUMBLUE: rColor = ARGBColor(0,0,205); return true;
        case XML_MEDIUMORCHID: rColor = ARGBColor(186,85,211); return true;
        case XML_MEDIUMPURPLE: rColor = ARGBColor(147,112,219); return true;
        case XML_MEDIUMSEAGREEN: rColor = ARGBColor(60,179,113); return true;
        case XML_MEDIUMSLATEBLUE: rColor = ARGBColor(123,104,238); return true;
        case XML_MEDIUMSPRINGGREEN: rColor = ARGBColor(0,250,154); return true;
        case XML_MEDIUMTURQUOISE: rColor = ARGBColor(72,209,204); return true;
        case XML_MEDIUMVIOLETRED: rColor = ARGBColor(199,21,133); return true;
        case XML_MIDNIGHTBLUE: rColor = ARGBColor(25,25,112); return true;
        case XML_MINTCREAM: rColor = ARGBColor(245,255,250); return true;
        case XML_MISTYROSE: rColor = ARGBColor(255,228,225); return true;
        case XML_MOCCASIN: rColor = ARGBColor(255,228,181); return true;
        case XML_NAVAJOWHITE: rColor = ARGBColor(255,222,173); return true;
        case XML_NAVY: rColor = ARGBColor(0,0,128); return true;
        case XML_OLDLACE: rColor = ARGBColor(253,245,230); return true;
        case XML_OLIVE: rColor = ARGBColor(128,128,0); return true;
        case XML_OLIVEDRAB: rColor = ARGBColor(107,142,35); return true;
        case XML_ORANGE: rColor = ARGBColor(255,165,0); return true;
        case XML_ORANGERED: rColor = ARGBColor(255,69,0); return true;
        case XML_ORCHID: rColor = ARGBColor(218,112,214); return true;
        case XML_PALEGOLDENROD: rColor = ARGBColor(238,232,170); return true;
        case XML_PALEGREEN: rColor = ARGBColor(152,251,152); return true;
        case XML_PALETURQUOISE: rColor = ARGBColor(175,238,238); return true;
        case XML_PALEVIOLETRED: rColor = ARGBColor(219,112,147); return true;
        case XML_PAPAYAWHIP: rColor = ARGBColor(255,239,213); return true;
        case XML_PEACHPUFF: rColor = ARGBColor(255,218,185); return true;
        case XML_PERU: rColor = ARGBColor(205,133,63); return true;
        case XML_PINK: rColor = ARGBColor(255,192,203); return true;
        case XML_PLUM: rColor = ARGBColor(221,160,221); return true;
        case XML_POWDERBLUE: rColor = ARGBColor(176,224,230); return true;
        case XML_PURPLE: rColor = ARGBColor(128,0,128); return true;
        case XML_RED: rColor = ARGBColor(255,0,0); return true;
        case XML_ROSYBROWN: rColor = ARGBColor(188,143,143); return true;
        case XML_ROYALBLUE: rColor = ARGBColor(65,105,225); return true;
        case XML_SADDLEBROWN: rColor = ARGBColor(139,69,19); return true;
        case XML_SALMON: rColor = ARGBColor(250,128,114); return true;
        case XML_SANDYBROWN: rColor = ARGBColor(244,164,96); return true;
        case XML_SEAGREEN: rColor = ARGBColor(46,139,87); return true;
        case XML_SEASHELL: rColor = ARGBColor(255,245,238); return true;
        case XML_SIENNA: rColor = ARGBColor(160,82,45); return true;
        case XML_SILVER: rColor = ARGBColor(192,192,192); return true;
        case XML_SKYBLUE: rColor = ARGBColor(135,206,235); return true;
        case XML_SLATEBLUE: rColor = ARGBColor(106,90,205); return true;
        case XML_SLATEGRAY: rColor = ARGBColor(112,128,144); return true;
        case XML_SLATEGREY: rColor = ARGBColor(112,128,144); return true;
        case XML_SNOW: rColor = ARGBColor(255,250,250); return true;
        case XML_SPRINGGREEN: rColor = ARGBColor(0,255,127); return true;
        case XML_STEELBLUE: rColor = ARGBColor(70,130,180); return true;
        case XML_TAN: rColor = ARGBColor(210,180,140); return true;
        case XML_TEAL: rColor = ARGBColor(0,128,128); return true;
        case XML_THISTLE: rColor = ARGBColor(216,191,216); return true;
        case XML_TOMATO: rColor = ARGBColor(255,99,71); return true;
        case XML_TURQUOISE: rColor = ARGBColor(64,224,208); return true;
        case XML_VIOLET: rColor = ARGBColor(238,130,238); return true;
        case XML_WHEAT: rColor = ARGBColor(245,222,179); return true;
        case XML_WHITE: rColor = ARGBColor(255,255,255); return true;
        case XML_WHITESMOKE: rColor = ARGBColor(245,245,245); return true;
        case XML_YELLOW: rColor = ARGBColor(255,255,0); return true;
        case XML_YELLOWGREEN: rColor = ARGBColor(154,205,50); return true;

        default:
            return false; // no color at all, I'd guess.
    }
}

bool parseOpacity (const char* sOpacity, ARGBColor& rColor )
{
    using namespace ::boost::spirit::classic;

    if( parse(sOpacity,
              // Begin grammar
              (
                  real_p[assign_a(rColor.a)]
                  ) >> end_p,
              // End grammar
              space_p).full )
    {
        return true;
    }
    return false;
}


bool parseTransform( const char* sTransform, basegfx::B2DHomMatrix& rTransform )
{
    using namespace ::boost::spirit::classic;

    double fRefOffsetX(0.0);
    double fRefOffsetY(0.0);
    bool   bRefTransform(false);

    double fRotationAngle=0.0;
    double fSkewAngle=0.0;
    geometry::AffineMatrix2D aIdentityTransform;
    geometry::AffineMatrix2D aCurrTransform;
    std::vector<geometry::AffineMatrix2D> aTransforms;
    aIdentityTransform.m00 = 1.0; aIdentityTransform.m11 = 1.0;
    aCurrTransform = aIdentityTransform;

    const bool bRes = parse(sTransform,
        //  Begin grammar
        (
            // identity transform
            str_p("none")
          |
            // the ref() form
            (str_p("ref")
             >> '('
             >> str_p("svg")[assign_a(bRefTransform,true)]
             >> !(real_p[assign_a(fRefOffsetX)] >> (',' | eps_p) >>
                  real_p[assign_a(fRefOffsetY)])
             >> ')')
          |
            // the transform-list form
            (list_p(
               (
                 // matrix(a,b,c,d,e,f)
                 (str_p("matrix")
                  >> '('
                  >> real_p[assign_a(aCurrTransform.m00)] >> (',' | eps_p)
                  >> real_p[assign_a(aCurrTransform.m10)] >> (',' | eps_p)
                  >> real_p[assign_a(aCurrTransform.m01)] >> (',' | eps_p)
                  >> real_p[assign_a(aCurrTransform.m11)] >> (',' | eps_p)
                  >> real_p[assign_a(aCurrTransform.m02)] >> (',' | eps_p)
                  >> real_p[assign_a(aCurrTransform.m12)]
                  >> ')')[push_back_a(aTransforms,aCurrTransform)]
               |
                 // translate(x,[y])
                 (str_p("translate")
                  >> '('
                  >> real_p[boost::bind(&assign_twice,
                                        boost::ref(aCurrTransform.m02),
                                        boost::ref(aCurrTransform.m12),_1)]
                  >> !((',' | eps_p) >> real_p[assign_a(aCurrTransform.m12)])
                  >> ')')[push_back_a(aTransforms,aCurrTransform)]
               |
                 // scale(x,[y])
                 (str_p("scale")
                  >> '('
                  >> real_p[boost::bind(&assign_twice,
                                        boost::ref(aCurrTransform.m00),
                                        boost::ref(aCurrTransform.m11),_1)]
                  >> !((',' | eps_p) >> real_p[assign_a(aCurrTransform.m11)])
                  >> ')')[push_back_a(aTransforms,aCurrTransform)]
               |
                 // rotate(phi,[cx, cy])
                 (str_p("rotate")
                  >> '('
                  >> real_p[assign_a(fRotationAngle)]
                  >> !((',' | eps_p) >> real_p[assign_a(aCurrTransform.m02)]
                       >> (',' | eps_p) >>  real_p[assign_a(aCurrTransform.m12)])
                  >> ')')[boost::bind(&calcRotation,
                                      boost::ref(aTransforms),
                                      boost::ref(aCurrTransform),
                                      boost::cref(fRotationAngle))]
               |
                 // skewX(phi)
                 (str_p("skewX")
                  >> '('
                  >> real_p[assign_a(fSkewAngle)]
                  >> ')')[boost::bind(&calcSkewX,
                                      boost::ref(aTransforms),
                                      boost::cref(fSkewAngle))]
               |
                 // skewY(phi)
                 (str_p("skewY")
                  >> '('
                  >> real_p[assign_a(fSkewAngle)]
                  >> ')')[boost::bind(&calcSkewY,
                                      boost::ref(aTransforms),
                                      boost::cref(fSkewAngle))]
                 // reset current transform after every push
               )[assign_a(aCurrTransform,aIdentityTransform)],
                 // list delimiter is either ',' or space
               ',' | eps_p ))
        ) >> end_p,
        //  End grammar
        space_p).full;

    if( !bRes )
        return false;

    // fold all transformations into one
    const geometry::AffineMatrix2D aTotalTransform(
        std::accumulate(aTransforms.begin(),
                        aTransforms.end(),
                        aIdentityTransform,
                        &multiplyMatrix));

    basegfx::unotools::homMatrixFromAffineMatrix(
        rTransform,
        aTotalTransform);

    // TODO(F1): handle the ref case
    return bRes;
}


bool parseViewBox( const char* sViewbox, basegfx::B2DRange& rRect )
{
    using namespace ::boost::spirit::classic;

    double x=0.0,y=0.0,w=0.0,h=0.0;

    const bool bRes = parse(sViewbox,
        //  Begin grammar
        (
            // either comma- or space-delimited list of four doubles
            real_p[assign_a(x)] >> (',' | eps_p) >>
            real_p[assign_a(y)] >> (',' | eps_p) >>
            real_p[assign_a(w)] >> (',' | eps_p) >>
            real_p[assign_a(h)] >> end_p
        ),
        //  End grammar
        space_p).full;

    if( !bRes )
        return false;

    rRect = basegfx::B2DRange(x,y,x+w,y+h);

    return true;
}


bool parseDashArray( const char* sDashArray, std::vector<double>& rOutputVector )
{
    using namespace ::boost::spirit::classic;

    rOutputVector.clear();
    return parse(sDashArray,
        //  Begin grammar
        (
            // parse comma-delimited list of doubles (have to use the
            // 'direct' variant, as otherwise spirit refactors our
            // parser to push both real num and comma to push_back_a)
            list_p.direct
            (
                real_p[push_back_a(rOutputVector)],
                // list delimiter is either ',' or space
                ',' | eps_p
            )
        ) >> end_p,
        //  End grammar
        space_p).full;
}


bool parsePaintUri( std::pair<const char*,const char*>& o_rPaintUri,
                    std::pair<ARGBColor,bool>&          io_rColor,
                    const char*                         sPaintUri )
{
    using namespace ::boost::spirit::classic;

    const bool bRes = parse(sPaintUri,
        //  Begin grammar
        (
            str_p("url(") >> !( str_p("'") ) >> "#" >>
            (+(anychar_p - (str_p("'") | str_p(")"))))[assign_a(o_rPaintUri)] >>
            !( str_p("'") ) >> str_p(")") >>
            *( str_p("none")[assign_a(io_rColor.second,false)] |
               str_p("currentColor")[assign_a(io_rColor.second,true)] |
               ColorGrammar(io_rColor.first)
               // TODO(F1): named color
             )
        ) >> end_p,
        //  End grammar
        space_p).full;

    return bRes;
}


bool parseXlinkHref( const char* sXlinkHref, OUString& data )
{
    OUString sLink(OUString::createFromAscii(sXlinkHref));

    if (sLink.startsWith("data:"))
    {
        sal_Int32 nIndex=0;
        OUString aCurrToken = sLink.getToken(0,',',nIndex);

        // the inplace "data" uri
        if ( !aCurrToken.isEmpty() )
        {
            data = sLink.copy(nIndex);
            SAL_INFO("filter.svg", data);
            return true;
        }
    }

    return false;
}

} // namespace svgi

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/filter/source/svg/svgfilter.cxx b/filter/source/svg/svgfilter.cxx
index 94fe876..1ec09f6 100644
--- a/filter/source/svg/svgfilter.cxx
+++ b/filter/source/svg/svgfilter.cxx
@@ -52,6 +52,12 @@
#include "svgfilter.hxx"
#include "svgwriter.hxx"

#include <svx/unopage.hxx>
#include <vcl/graphicfilter.hxx>
#include <svx/svdpage.hxx>
#include <svx/svdograf.hxx>
#include <svl/itempool.hxx>

#include <memory>

using namespace ::com::sun::star;
@@ -94,14 +100,185 @@ SVGFilter::~SVGFilter()
sal_Bool SAL_CALL SVGFilter::filter( const Sequence< PropertyValue >& rDescriptor )
{
    SolarMutexGuard aGuard;
    vcl::Window*     pFocusWindow = Application::GetFocusWindow();
    bool    bRet;
    vcl::Window* pFocusWindow(Application::GetFocusWindow());
    bool bRet(false);

    if( pFocusWindow )
    if(pFocusWindow)
    {
        pFocusWindow->EnterWait();
    }

    if( mxDstDoc.is() )
        bRet = implImport( rDescriptor );
    if(mxDstDoc.is())
    {
        // Import. Use an endless loop to have easy exits for error handling
        while(true)
        {
            // use MediaDescriptor to get needed data out of Sequence< PropertyValue >
            utl::MediaDescriptor aMediaDescriptor(rDescriptor);
            uno::Reference<io::XInputStream> xInputStream;
            uno::Reference<task::XStatusIndicator> xStatus;

            xInputStream.set(aMediaDescriptor[utl::MediaDescriptor::PROP_INPUTSTREAM()], UNO_QUERY);
            xStatus.set(aMediaDescriptor[utl::MediaDescriptor::PROP_STATUSINDICATOR()], UNO_QUERY);

            if(!xInputStream.is() || !xStatus.is())
            {
                // we need the InputStream and StatusIndicator
                break;
            }

            // get the DrawPagesSupplier
            uno::Reference< drawing::XDrawPagesSupplier > xDrawPagesSupplier( mxDstDoc, uno::UNO_QUERY );

            if(!xDrawPagesSupplier.is())
            {
                // we need the DrawPagesSupplier
                break;
            }

            // get the DrawPages
            uno::Reference< drawing::XDrawPages > xDrawPages( xDrawPagesSupplier->getDrawPages(), uno::UNO_QUERY );

            if(!xDrawPages.is())
            {
                // we need the DrawPages
                break;
            }

            // check DrawPageCount (there should be one by default)
            sal_Int32 nDrawPageCount(xDrawPages->getCount());

            if(0 == nDrawPageCount)
            {
                // at least one DrawPage should be there - we need that
                break;
            }

            // get that DrawPage
            uno::Reference< drawing::XDrawPage > xDrawPage( xDrawPages->getByIndex(0), uno::UNO_QUERY );

            if(!xDrawPage.is())
            {
                // we need that DrawPage
                break;
            }

            // get that DrawPage's UNO API implementation
            SvxDrawPage* pSvxDrawPage(SvxDrawPage::getImplementation(xDrawPage));

            if(nullptr == pSvxDrawPage || nullptr == pSvxDrawPage->GetSdrPage())
            {
                // we need a SvxDrawPage
                break;
            }

            // get the SvStream to work with
            std::unique_ptr< SvStream > aStream(utl::UcbStreamHelper::CreateStream(xInputStream, true));

            if(!aStream.get())
            {
                // we need the SvStream
                break;
            }

            // create a GraphicFilter and load the SVG (format already known, thus *could*
            // be handed over to ImportGraphic - but detection is fast).
            // As a bonus, zipped data is already detected and handled there
            GraphicFilter aGraphicFilter;
            Graphic aGraphic;
            const ErrCode nGraphicFilterErrorCode(aGraphicFilter.ImportGraphic(aGraphic, OUString(), *aStream.get()));

            if(ERRCODE_NONE != nGraphicFilterErrorCode)
            {
                // SVG import error, cannot continue
                break;
            }

            // get the GraphicPrefSize early to check if we have any content
            // (the SVG may contain nothing and/or just <g visibility="hidden"> stuff...)
            const Size aGraphicPrefSize(aGraphic.GetPrefSize());

            if(0 == aGraphicPrefSize.Width() || 0 == aGraphicPrefSize.Height())
            {
                // SVG has no displayable content, stop import.
                // Also possible would be to get the sequence< Primitives >
                // from aGraphic and check if it is empty.
                // Possibility to set some error message here to tell
                // the user what/why loading went wrong, but I do not
                // know how this could be done here
                break;
            }

            // create a SdrModel-GraphicObject to insert to page
            SdrPage* pTargetSdrPage(pSvxDrawPage->GetSdrPage());
            std::unique_ptr< SdrGrafObj, SdrObjectFreeOp > aNewSdrGrafObj(
                new SdrGrafObj(
                    pTargetSdrPage->getSdrModelFromSdrPage(),
                    aGraphic));

            if(!aNewSdrGrafObj.get())
            {
                // could not create GraphicObject
                break;
            }

            // Evtl. adapt the GraphicPrefSize to target-MapMode of target-Model
            // (should be 100thmm here, but just stay safe by doing the conversion)
            const MapMode aGraphicPrefMapMode(aGraphic.GetPrefMapMode());
            const MapUnit eDestUnit(pTargetSdrPage->getSdrModelFromSdrPage().GetItemPool().GetMetric(0));
            const MapUnit eSrcUnit(aGraphicPrefMapMode.GetMapUnit());
            Size aGraphicSize(aGraphicPrefSize);

            if (eDestUnit != eSrcUnit)
            {
                aGraphicSize = Size(
                    OutputDevice::LogicToLogic(aGraphicSize.Width(), eSrcUnit, eDestUnit),
                    OutputDevice::LogicToLogic(aGraphicSize.Height(), eSrcUnit, eDestUnit));
            }

            // Based on GraphicSize, set size of Page. Do not forget to adapt PageBorders,
            // but interpret them relative to PageSize so that they do not 'expolde/shrink'
            // in comparison. Use a common scaling factor for hor/ver to not get
            // asynchronous border distances, though. All in all this will adapt borders
            // nicely and is based on office-defaults for standard-page-border-sizes.
            const Size aPageSize(pTargetSdrPage->GetSize());
            const double fBorderRelation((
                static_cast< double >(pTargetSdrPage->GetLeftBorder()) / aPageSize.Width() +
                static_cast< double >(pTargetSdrPage->GetRightBorder()) / aPageSize.Width() +
                static_cast< double >(pTargetSdrPage->GetUpperBorder()) / aPageSize.Height() +
                static_cast< double >(pTargetSdrPage->GetLowerBorder()) / aPageSize.Height()) / 4.0);
            const long nAllBorder(basegfx::fround((aGraphicSize.Width() + aGraphicSize.Height()) * fBorderRelation * 0.5));

            // Adapt PageSize and Border stuff. To get all MasterPages and PresObjs
            // correctly adapted, do not just use
            //      pTargetSdrPage->SetBorder(...) and
            //      pTargetSdrPage->SetSize(...),
            // but ::adaptSizeAndBorderForAllPages
            pTargetSdrPage->getSdrModelFromSdrPage().adaptSizeAndBorderForAllPages(
                Size(
                    aGraphicSize.Width() + nAllBorder + nAllBorder,
                    aGraphicSize.Height() + nAllBorder + nAllBorder),
                nAllBorder,
                nAllBorder,
                nAllBorder,
                nAllBorder);

            // set pos/size at SdrGraphicObj - add offset to PageBorder
            aNewSdrGrafObj->SetSnapRect(
                tools::Rectangle(
                    Point(nAllBorder, nAllBorder),
                    aGraphicSize));

            // insert to page (owner change of SdrGrafObj)
            pTargetSdrPage->InsertObject(aNewSdrGrafObj.release());

            // done - set positive result now
            bRet = true;

            // always leave helper endless loop
            break;
        };
    }
    else if( mxSrcDoc.is() )
    {
        // #i124608# detect selection
diff --git a/filter/source/svg/svgfilter.hxx b/filter/source/svg/svgfilter.hxx
index 63616a9..5f117c3 100644
--- a/filter/source/svg/svgfilter.hxx
+++ b/filter/source/svg/svgfilter.hxx
@@ -223,9 +223,6 @@ private:
    Link<EditFieldInfo*,void>           maNewFieldHdl;

    /// @throws css::uno::RuntimeException
    bool                            implImport( const Sequence< PropertyValue >& rDescriptor );

    /// @throws css::uno::RuntimeException
    bool                            implExport( const Sequence< PropertyValue >& rDescriptor );
    static Reference< XWriter >     implCreateExportDocumentHandler( const Reference< XOutputStream >& rxOStm );

diff --git a/filter/source/svg/svgimport.cxx b/filter/source/svg/svgimport.cxx
deleted file mode 100644
index 5b39d2a..0000000
--- a/filter/source/svg/svgimport.cxx
+++ /dev/null
@@ -1,119 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */


#include "svgfilter.hxx"
#include <svgreader.hxx>

#include <rtl/ref.hxx>

#include <com/sun/star/lang/XMultiServiceFactory.hpp>

#include <com/sun/star/lang/XComponent.hpp>

#include <com/sun/star/uno/Any.hxx>

#include <com/sun/star/beans/PropertyValue.hpp>

#include <com/sun/star/xml/sax/XParser.hpp>
#include <com/sun/star/xml/sax/InputSource.hpp>
#include <com/sun/star/xml/XImportFilter.hpp>

#include <com/sun/star/io/XActiveDataSource.hpp>
#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
#include <com/sun/star/task/XStatusIndicator.hpp>

#include <unotools/mediadescriptor.hxx>
#include <unotools/ucbstreamhelper.hxx>
#include <tools/zcodec.hxx>

#include <memory>

using namespace ::com::sun::star;
using namespace ::svgi;

bool SVGFilter::implImport(const Sequence< PropertyValue >& rDescriptor)
{
    utl::MediaDescriptor aMediaDescriptor(rDescriptor);
    uno::Reference<io::XInputStream> xInputStream;
    uno::Reference<task::XStatusIndicator> xStatus;

    xInputStream.set(aMediaDescriptor[utl::MediaDescriptor::PROP_INPUTSTREAM()], UNO_QUERY);
    xStatus.set(aMediaDescriptor[utl::MediaDescriptor::PROP_STATUSINDICATOR()], UNO_QUERY);

    if (isStreamGZip(xInputStream))
    {
        uno::Reference<io::XSeekable> xSeek(xInputStream, uno::UNO_QUERY);
        if (!xSeek.is())
            return false;
        xSeek->seek(0);

        std::unique_ptr<SvStream> aStream(utl::UcbStreamHelper::CreateStream(xInputStream, true ));
        if(!aStream.get())
            return false;

        SvStream* pMemoryStream = new SvMemoryStream;
        ZCodec aCodec;
        aCodec.BeginCompression(ZCODEC_DEFAULT_COMPRESSION, false, true);
        aCodec.Decompress(*aStream.get(), *pMemoryStream);
        aCodec.EndCompression();
        pMemoryStream->Seek(STREAM_SEEK_TO_BEGIN);
        uno::Reference<io::XInputStream> xDecompressedInput(new utl::OSeekableInputStreamWrapper(pMemoryStream, true));
        if (!xDecompressedInput.is())
            return false;
        xInputStream = xDecompressedInput;
    }
    else
    {
        uno::Reference<io::XSeekable> xSeek(xInputStream, uno::UNO_QUERY);
        if (xSeek.is())
            xSeek->seek(0);
    }

    OSL_ASSERT(xInputStream.is());
    if(!xInputStream.is())
        return false;

    Reference < XDocumentHandler > xInternalHandler(
        mxContext->getServiceManager()->createInstanceWithContext( "com.sun.star.comp.Draw.XMLOasisImporter", mxContext ), UNO_QUERY );

    // The XImporter sets up an empty target document for XDocumentHandler to write to..
    uno::Reference < XImporter > xImporter(xInternalHandler, UNO_QUERY);
    xImporter->setTargetDocument(mxDstDoc);

    bool bRet = false;
    SVGReader aReader(mxContext, xInputStream, xInternalHandler);
    try
    {
        bRet = aReader.parseAndConvert();
    }
    catch (const RuntimeException&)
    {
        throw;
    }
    catch (const Exception& e)
    {
        throw css::lang::WrappedTargetRuntimeException("SVGFilter::implImport non-RuntimeException",
            static_cast<uno::XWeak*>(this), uno::makeAny(e));
    }
    return bRet;
}


/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/filter/source/svg/svgreader.cxx b/filter/source/svg/svgreader.cxx
deleted file mode 100644
index 1bffc82..0000000
--- a/filter/source/svg/svgreader.cxx
+++ /dev/null
@@ -1,2304 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include <svgreader.hxx>
#include <xmloff/attrlist.hxx>
#include <gfxtypes.hxx>
#include "units.hxx"
#include <parserfragments.hxx>
#include "tokenmap.hxx"
#include "b2dellipse.hxx"

#include <rtl/math.hxx>
#include <rtl/ref.hxx>
#include <rtl/ustring.hxx>
#include <rtl/ustrbuf.hxx>
#include <basegfx/vector/b2enums.hxx>
#include <basegfx/range/b2drange.hxx>
#include <basegfx/matrix/b2dhommatrix.hxx>
#include <basegfx/polygon/b2dpolypolygon.hxx>
#include <basegfx/polygon/b2dlinegeometry.hxx>
#include <basegfx/polygon/b2dpolygontools.hxx>
#include <basegfx/polygon/b2dpolypolygontools.hxx>
#include <com/sun/star/io/XSeekable.hpp>
#include <com/sun/star/xml/sax/XParser.hpp>
#include <com/sun/star/xml/dom/DocumentBuilder.hpp>
#include <com/sun/star/xml/dom/NodeType.hpp>

#include <basegfx/polygon/b2dpolygoncutandtouch.hxx>
#include <basegfx/polygon/b2dpolypolygoncutter.hxx>
#include <unotools/streamwrap.hxx>
#include <sax/tools/converter.hxx>
#include <vcl/graph.hxx>
#include <vcl/virdev.hxx>
#include <vcl/gradient.hxx>
#include <vcl/graphicfilter.hxx>
#include <tools/zcodec.hxx>

#include <map>

#define OASIS_STR "urn:oasis:names:tc:opendocument:xmlns:"

using namespace ::com::sun::star;

namespace svgi
{
    enum SvgiVisitorCaller {STYLE_ANNOTATOR, SHAPE_WRITER, STYLE_WRITER};
namespace
{

/** visits all children of the specified type with the given functor
 */
template<typename Func> void visitChildren(const Func& rFunc,
                                           const uno::Reference<xml::dom::XElement>& rElem,
                                           xml::dom::NodeType eChildType )
{
    uno::Reference<xml::dom::XNodeList> xChildren( rElem->getChildNodes() );
    const sal_Int32 nNumNodes( xChildren->getLength() );
    for( sal_Int32 i=0; i<nNumNodes; ++i )
    {
        SAL_INFO("filter.svg", "node type: " << sal::static_int_cast<sal_uInt32>(xChildren->item(i)->getNodeType()) << " tag name " << xChildren->item(i)->getNodeName() << " value |" << xChildren->item(i)->getNodeValue() << "|");
        if( xChildren->item(i)->getNodeType() == eChildType )
            rFunc( *xChildren->item(i).get() );
    }
}

/** Visit all elements of the given tree (in-order traversal)

    Given functor is called for every element, and passed the
    element's attributes, if any
 */
template<typename Func> void visitElements(Func& rFunc,
                                           const uno::Reference<xml::dom::XElement>& rElem,
                                           SvgiVisitorCaller eCaller)
{
    if( rElem->hasAttributes() )
        rFunc(rElem, rElem->getAttributes());
    else
        rFunc(rElem);

    // notify children processing
    rFunc.push();

    if (eCaller == SHAPE_WRITER && rElem->getTagName() == "defs")
        return;

    // recurse over children
    uno::Reference<xml::dom::XNodeList> xChildren( rElem->getChildNodes() );
    const sal_Int32 nNumNodes( xChildren->getLength() );
    for( sal_Int32 i=0; i<nNumNodes; ++i )
    {
        if( xChildren->item(i)->getNodeType() == xml::dom::NodeType_ELEMENT_NODE ){
            visitElements( rFunc,
                uno::Reference<xml::dom::XElement>(
                xChildren->item(i),
                uno::UNO_QUERY_THROW),
                eCaller );
        }
    }

    // children processing done
    rFunc.pop();
}

template<typename value_type> value_type square(value_type v)
{
    return v*v;
}

double colorDiffSquared(const ARGBColor& rCol1, const ARGBColor& rCol2)
{
    return
        square(rCol1.a-rCol2.a)
        + square(rCol1.r-rCol2.r)
        + square(rCol1.g-rCol2.g)
        + square(rCol1.b-rCol2.b);
}

/**
    check whether a polypolygon contains both open and closed
    polygons
**/
bool PolyPolygonIsMixedOpenAndClosed( const basegfx::B2DPolyPolygon& rPoly )
{
    bool bRetval(false);
    bool bOpen(false);
    bool bClosed(false);

    // PolyPolygon is mixed open and closed if there is more than one
    // polygon and there are both closed and open polygons.
    for( sal_uInt32 a(0); !bRetval && a < rPoly.count(); a++ )
    {
        if ( rPoly.getB2DPolygon(a).isClosed() )
        {
            bClosed = true;
        }
        else
        {
            bOpen = true;
        }

        bRetval = (bClosed && bOpen);
    }

    return bRetval;
}

typedef std::map<OUString,std::size_t> ElementRefMapType;

struct AnnotatingVisitor
{
    AnnotatingVisitor(StatePool&                                        rStatePool,
                      StateMap&                                         rStateMap,
                      const State&                                       rInitialState,
                      const uno::Reference<xml::sax::XDocumentHandler>& xDocumentHandler,
                      std::vector< uno::Reference<xml::dom::XElement> >& rUseElementVector,
                      bool& rGradientNotFound) :
        mnCurrStateId(0),
        maCurrState(),
        maParentStates(),
        mrStates(rStatePool),
        mrStateMap(rStateMap),
        mxDocumentHandler(xDocumentHandler),
        maGradientVector(),
        maGradientStopVector(),
        maElementVector(),
        mrUseElementVector(rUseElementVector),
        mrGradientNotFound(rGradientNotFound)
    {
        maParentStates.push_back(rInitialState);
    }

    void operator()( const uno::Reference<xml::dom::XElement>& xElem)
    {
        const sal_Int32 nTagId(getTokenId(xElem->getTagName()));
        if (nTagId != XML_TEXT && nTagId != XML_TSPAN)
            return;

        maCurrState = maParentStates.back();
        maCurrState.maTransform.identity();
        maCurrState.maViewBox.reset();
        // if necessary, serialize to automatic-style section
        writeStyle(xElem,nTagId);
    }

    bool IsAncestorId(const uno::Reference<xml::dom::XNode>& xParentNode, const OUString& rValue)
    {
        bool bSelfCycle = false;
        if (xParentNode.is() && xParentNode->getNodeType() == xml::dom::NodeType_ELEMENT_NODE)
        {
            if (xParentNode->hasAttributes())
            {
                const uno::Reference<xml::dom::XNamedNodeMap> xParentAttributes = xParentNode->getAttributes();
                const sal_Int32 nFooNumAttrs(xParentAttributes->getLength());
                for (sal_Int32 i=0; i < nFooNumAttrs; ++i)
                {
                    const sal_Int32 nTokenId(getTokenId(xParentAttributes->item(i)->getNodeName()));
                    if (XML_ID == nTokenId)
                    {
                        OUString sParentID = xParentAttributes->item(i)->getNodeValue();
                        bSelfCycle = sParentID == rValue;
                        break;
                    }
                }
            }

            if (!bSelfCycle)
                bSelfCycle = IsAncestorId(xParentNode->getParentNode(), rValue);
        }
        return bSelfCycle;
    }

    void operator()( const uno::Reference<xml::dom::XElement>&      xElem,
                     const uno::Reference<xml::dom::XNamedNodeMap>& xAttributes )
    {
        const sal_Int32 nTagId(getTokenId(xElem->getTagName()));
        switch (nTagId)
        {
            case XML_LINEARGRADIENT:
            {
                const sal_Int32 nNumAttrs( xAttributes->getLength() );
                maGradientVector.emplace_back(Gradient::LINEAR);

                // do we have a reference to a parent gradient? parse
                // that first, as it sets our defaults here (manually
                // tracking default state on each Gradient variable is
                // much more overhead)
                uno::Reference<xml::dom::XNode> xNode(xAttributes->getNamedItem("href"));
                if(xNode.is())
                {
                    const OUString sValue(xNode->getNodeValue());
                    ElementRefMapType::iterator aFound=maGradientIdMap.end();
                    if ( sValue.startsWith("#") )
                        aFound = maGradientIdMap.find(sValue.copy(1));
                    else
                        aFound = maGradientIdMap.find(sValue);

                    if( aFound != maGradientIdMap.end() )
                        maGradientVector.back() = maGradientVector[aFound->second];
                    else
                    {
                        mrGradientNotFound = true;
                        maGradientVector.pop_back();
                        return;
                    }
                }

                // do that after dereferencing, to prevent hyperlinked
                // gradient to clobber our Id again
                maGradientVector.back().mnId = maGradientVector.size()-1;
                maGradientVector.back().meType = Gradient::LINEAR; // has been clobbered as well

                for( sal_Int32 i=0; i<nNumAttrs; ++i )
                {
                    parseLinearGradientData( maGradientVector.back(),
                                             maGradientVector.size()-1,
                                             getTokenId(xAttributes->item(i)->getNodeName()),
                                             xAttributes->item(i)->getNodeValue() );
                }
                break;
            }
            case XML_RADIALGRADIENT:
            {
                const sal_Int32 nNumAttrs( xAttributes->getLength() );
                maGradientVector.emplace_back(Gradient::RADIAL);

                // do we have a reference to a parent gradient? parse
                // that first, as it sets our defaults here (manually
                // tracking default state on each Gradient variable is
                // much more overhead)
                uno::Reference<xml::dom::XNode> xNode(xAttributes->getNamedItem("href"));
                if(xNode.is())
                {
                    const OUString sValue(xNode->getNodeValue());
                    ElementRefMapType::iterator aFound=maGradientIdMap.end();
                    if ( sValue.startsWith("#") )
                        aFound = maGradientIdMap.find(sValue.copy(1));
                    else
                        aFound = maGradientIdMap.find(sValue);

                    if( aFound != maGradientIdMap.end() )
                        maGradientVector.back() = maGradientVector[aFound->second];
                    else
                    {
                        mrGradientNotFound = true;
                        maGradientVector.pop_back();
                        return;
                    }
                }

                // do that after dereferencing, to prevent hyperlinked
                // gradient to clobber our Id again
                maGradientVector.back().mnId = maGradientVector.size()-1;
                maGradientVector.back().meType = Gradient::RADIAL; // has been clobbered as well

                for( sal_Int32 i=0; i<nNumAttrs; ++i )
                {
                    parseRadialGradientData( maGradientVector.back(),
                                             maGradientVector.size()-1,
                                             getTokenId(xAttributes->item(i)->getNodeName()),
                                             xAttributes->item(i)->getNodeValue() );
                }
                break;
            }
            case XML_USE:
            {
                uno::Reference<xml::dom::XNode> xNode(xAttributes->getNamedItem("href"));
                if(xNode.is())
                {
                    OUString sValue(xNode->getNodeValue());
                    ElementRefMapType::iterator aFound=maElementIdMap.end();
                    if ( sValue.startsWith("#") )
                        sValue = sValue.copy(1);
                    aFound = maElementIdMap.find(sValue);
                    bool bFound = aFound != maElementIdMap.end();
                    if (bFound)
                    {
                        const bool bSelfCycle = IsAncestorId(xElem->getParentNode(), sValue);
                        if (bSelfCycle)
                        {
                            SAL_WARN("filter.svg", "\"use\" declaration with target of ancestor node, causing use cycle");
                            //drop this invalid self-referencing "use" node
                            maElementIdMap.erase(aFound);
                            bFound = false;
                        }
                    }

                    if (bFound)
                    {
                        uno::Reference<xml::dom::XElement> xRefElem(
                            maElementVector[aFound->second]->cloneNode(true), uno::UNO_QUERY);

                        xRefElem->removeAttribute("id");

                        const sal_Int32 nNumAttrs( xAttributes->getLength() );
                        OUString sAttributeValue;
                        double x=0.0, y=0.0;
                        for( sal_Int32 i=0; i<nNumAttrs; ++i )
                        {
                            sAttributeValue = xAttributes->item(i)->getNodeValue();
                            const sal_Int32 nAttribId(
                                getTokenId(xAttributes->item(i)->getNodeName()));

                            switch(nAttribId)
                            {
                                case XML_ID:
                                    maElementVector.push_back(xElem);
                                    maElementIdMap.insert(std::make_pair(sAttributeValue,
                                        maElementVector.size() - 1));
                                    break;
                                case XML_X:
                                    x = convLength(sAttributeValue,maCurrState,'h');
                                    break;
                                case XML_Y:
                                    y = convLength(sAttributeValue,maCurrState,'v');
                                    break;
                                case XML_TRANSFORM:
                                    break;
                                default:
                                    OUString sAttributeName = xAttributes->item(i)->getNodeName();
                                    xRefElem->setAttribute(sAttributeName, sAttributeValue);
                                    break;
                            }
                        }
                        std::stringstream ssAttrValue;
                        ssAttrValue << xRefElem->getAttribute("transform");
                        ssAttrValue << xElem->getAttribute("transform");
                        ssAttrValue << " translate(" << x << "," << y << ")";

                        OUString attrValue(OUString::createFromAscii (ssAttrValue.str().c_str()));
                        xRefElem->setAttribute("transform", attrValue);

                        mrUseElementVector.push_back(xRefElem);

                        visitElements((*this), xRefElem, STYLE_ANNOTATOR);
                    }
                }
                break;
            }
            case XML_STOP:
            {
                if (!maGradientVector.empty())
                {
                    const sal_Int32 nNumAttrs( xAttributes->getLength() );
                    maGradientStopVector.emplace_back();
                    maGradientVector.back().maStops.push_back(maGradientStopVector.size()-1);

                    // first parse 'color' as 'stop-color' might depend on it
                    // if 'stop-color''s value is "currentColor" and parsed previously
                    uno::Reference<xml::dom::XNode> xNodeColor(xAttributes->getNamedItem("color"));
                    if(xNodeColor.is())
                        parseGradientStop( maGradientStopVector.back(),
                            maGradientStopVector.size()-1,
                            XML_STOP_COLOR,
                            xNodeColor->getNodeValue() );

                    //now, parse the rest of attributes
                    for( sal_Int32 i=0; i<nNumAttrs; ++i )
                    {
                        const sal_Int32 nTokenId(
                            getTokenId(xAttributes->item(i)->getNodeName()));
                        if ( nTokenId != XML_COLOR )
                            parseGradientStop( maGradientStopVector.back(),
                                               maGradientStopVector.size()-1,
                                               nTokenId,
                                               xAttributes->item(i)->getNodeValue() );
                    }
                }
                break;
            }
            default:
            {
                if ( !mrGradientNotFound )
                {
                    // init state. inherit defaults from parent.
                    maCurrState = maParentStates.back();
                    maCurrState.maTransform.identity();
                    maCurrState.maViewBox.reset();

                    // first parse 'color' and 'style' as 'fill' and 'stroke' might depend on them
                    // if their values are "currentColor" and parsed previously
                    uno::Reference<xml::dom::XNode> xNodeColor(xAttributes->getNamedItem("color"));
                    if(xNodeColor.is())
                        parseAttribute(XML_COLOR, xNodeColor->getNodeValue());

                    uno::Reference<xml::dom::XNode> xNodeStyle(xAttributes->getNamedItem("style"));
                    if(xNodeStyle.is())
                        parseStyle(xNodeStyle->getNodeValue());

                    const sal_Int32 nNumAttrs( xAttributes->getLength() );
                    OUString sAttributeValue;

                    //now, parse the rest of attributes
                    for( sal_Int32 i=0; i<nNumAttrs; ++i )
                    {
                        sAttributeValue = xAttributes->item(i)->getNodeValue();
                        const sal_Int32 nTokenId(
                            getTokenId(xAttributes->item(i)->getNodeName()));
                        if( XML_ID == nTokenId )
                        {
                            maElementVector.push_back(xElem);
                            maElementIdMap.insert(std::make_pair(sAttributeValue,
                                maElementVector.size() - 1));
                        }
                        else if (nTokenId != XML_COLOR && nTokenId != XML_STYLE)
                            parseAttribute(nTokenId,
                                sAttributeValue);
                    }

                    // all attributes parsed, can calc total CTM now
                    basegfx::B2DHomMatrix aLocalTransform;
                    if( !maCurrState.maViewBox.isEmpty() &&
                        maCurrState.maViewBox.getWidth() != 0.0 &&
                        maCurrState.maViewBox.getHeight() != 0.0 )
                    {
                        // transform aViewBox into viewport, keep aspect ratio
                        aLocalTransform.translate(-maCurrState.maViewBox.getMinX(),
                                                  -maCurrState.maViewBox.getMinY());
                        double scaleW = maCurrState.maViewport.getWidth()/maCurrState.maViewBox.getWidth();
                        double scaleH = maCurrState.maViewport.getHeight()/maCurrState.maViewBox.getHeight();
                        double scale = (scaleW < scaleH) ? scaleW : scaleH;
                        aLocalTransform.scale(scale,scale);
                    }
                    else if( !maParentStates.back().maViewBox.isEmpty() )
                                        maCurrState.maViewBox = maParentStates.back().maViewBox;

                    maCurrState.maCTM = maCurrState.maCTM*maCurrState.maTransform*aLocalTransform;

                    // if necessary, serialize to automatic-style section
                    writeStyle(xElem,nTagId);
                }
            }
        }
    }

    static OUString getStyleName( const char* sPrefix, sal_Int32 nId )
    {
        return OUString::createFromAscii(sPrefix)+OUString::number(nId);
    }

    bool hasGradientOpacity( const Gradient& rGradient )
    {
        return
            (rGradient.maStops.size() > 1) &&
            (maGradientStopVector[
                 rGradient.maStops[0]].maStopColor.a != 1.0 ||
             maGradientStopVector[
                 rGradient.maStops[1]].maStopColor.a != 1.0);
    }

    struct StopSorter
    {
        explicit StopSorter( const std::vector< GradientStop >& rStopVec ) :
            mrStopVec(rStopVec)
        {}

        bool operator()( std::size_t rLHS, std::size_t rRHS )
        {
            return mrStopVec[rLHS].mnStopPosition < mrStopVec[rRHS].mnStopPosition;
        }

        const std::vector< GradientStop >& mrStopVec;
    };

    void optimizeGradientStops( Gradient& rGradient )
    {
        // sort for increasing stop position
        std::sort(rGradient.maStops.begin(),rGradient.maStops.end(),
                  StopSorter(maGradientStopVector));

        if( rGradient.maStops.size() < 3 )
            return; //easy! :-)

        // join similar colors
        std::vector<std::size_t> aNewStops { rGradient.maStops.front() };
        for( std::size_t i=1; i<rGradient.maStops.size(); ++i )
        {
            if( maGradientStopVector[rGradient.maStops[i]].maStopColor !=
                maGradientStopVector[aNewStops.back()].maStopColor )
                aNewStops.push_back(rGradient.maStops[i]);
        }

        rGradient.maStops = aNewStops;
        if (rGradient.maStops.size() < 2)
        {
            return; // can't optimize further...
        }

        // axial gradient, maybe?
        if( rGradient.meType == Gradient::LINEAR &&
            rGradient.maStops.size() == 3 &&
            maGradientStopVector[rGradient.maStops.front()].maStopColor ==
            maGradientStopVector[rGradient.maStops.back()].maStopColor )
        {
            // yep - keep it at that
            return;
        }

        // find out most significant color difference, and limit to
        // those two stops around this border (metric is
        // super-simplistic: take euclidean distance of colors, weigh
        // with stop distance)
        std::size_t nMaxIndex=0;
        double    fMaxDistance=0.0;
        for( std::size_t i=1; i<rGradient.maStops.size(); ++i )
        {
            const double fCurrDistance(
                colorDiffSquared(
                    maGradientStopVector[rGradient.maStops[i-1]].maStopColor,
                    maGradientStopVector[rGradient.maStops[i]].maStopColor) *
                (square(maGradientStopVector[rGradient.maStops[i-1]].mnStopPosition) +
                 square(maGradientStopVector[rGradient.maStops[i]].mnStopPosition)) );

            if( fCurrDistance > fMaxDistance )
            {
                nMaxIndex = i-1;
                fMaxDistance = fCurrDistance;
            }
        }
        rGradient.maStops[0] = rGradient.maStops[nMaxIndex];
        rGradient.maStops[1] = rGradient.maStops[nMaxIndex+1];
        rGradient.maStops.erase(rGradient.maStops.begin()+2,rGradient.maStops.end());
    }

    static sal_Int8 toByteColor( double val )
    {
        // TODO(Q3): duplicated from vcl::unotools
        return sal::static_int_cast<sal_Int8>(
            basegfx::fround(val*255.0));
    }

    static OUString getOdfColor( const ARGBColor& rColor )
    {
        // TODO(Q3): duplicated from pdfimport
        OUStringBuffer aBuf( 7 );
        const sal_uInt8 nRed  ( toByteColor(rColor.r)   );
        const sal_uInt8 nGreen( toByteColor(rColor.g) );
        const sal_uInt8 nBlue ( toByteColor(rColor.b)  );
        aBuf.append( '#' );
        if( nRed < 0x10 )
            aBuf.append( '0' );
        aBuf.append( sal_Int32(nRed), 16 );
        if( nGreen < 0x10 )
            aBuf.append( '0' );
        aBuf.append( sal_Int32(nGreen), 16 );
        if( nBlue < 0x10 )
            aBuf.append( '0' );
        aBuf.append( sal_Int32(nBlue), 16 );

        // TODO(F3): respect alpha transparency (polygons etc.)
        OSL_ASSERT(rColor.a == 1.0);

        return aBuf.makeStringAndClear();
    }

    static OUString getOdfAlign( TextAlign eAlign )
    {
        switch(eAlign)
        {
            default:
            case BEFORE:
                return OUString("start");
            case CENTER:
                return OUString("center");
            case AFTER:
                return OUString("end");
        }
    }

    bool writeStyle(State& rState, const sal_Int32 nTagId)
    {
        rtl::Reference<SvXMLAttributeList> xAttrs( new SvXMLAttributeList() );
        uno::Reference<xml::sax::XAttributeList> xUnoAttrs( xAttrs.get() );

        if (XML_TEXT == nTagId || XML_TSPAN == nTagId) {
            rState.mbIsText = true;
            basegfx::B2DTuple aScale, aTranslate;
            double fRotate, fShearX;
            if (rState.maCTM.decompose(aScale, aTranslate, fRotate, fShearX))
            {
                rState.mnFontSize *= aScale.getX();
            }
        }

        std::pair<StatePool::iterator,
                  bool> aRes = mrStates.insert(rState);
        SAL_INFO ("filter.svg", "size " << mrStates.size() << "   id " <<  const_cast<State&>(*aRes.first).mnStyleId);

        if( !aRes.second )
            return false; // not written

        ++mnCurrStateId;

        // mnStyleId does not take part in hashing/comparison
        const_cast<State&>(*aRes.first).mnStyleId = mnCurrStateId;
        SAL_INFO ("filter.svg", " --> " <<  const_cast<State&>(*aRes.first).mnStyleId);

        mrStateMap.insert(std::make_pair(
                              mnCurrStateId,
                              rState));

        // find two representative stop colors (as ODF only support
        // start&end color)
        optimizeGradientStops(rState.maFillGradient);

        if( !mxDocumentHandler.is() )
            return true; // cannot write style, svm import case

        // do we have a gradient fill? then write out gradient as well
        if( rState.meFillType == GRADIENT && rState.maFillGradient.maStops.size() > 0 )
        {
            // if only one stop-color is defined
            if( rState.maFillGradient.maStops.size() == 1 )
                rState.maFillGradient.maStops.push_back(rState.maFillGradient.maStops[0]);

            // TODO(F3): ODF12 supposedly also groks svg:linear/radialGradient. But CL says: nope.
            xAttrs->AddAttribute( "draw:name", getStyleName("svggradient", rState.maFillGradient.mnId) );
            if( rState.maFillGradient.meType == Gradient::LINEAR )
            {
                // should the optimizeGradientStops method decide that
                // this is a three-color gradient, it prolly wanted us
                // to take axial instead
                xAttrs->AddAttribute( "draw:style",
                                      rState.maFillGradient.maStops.size() == 3 ?
                                      OUString("axial") :
                                      OUString("linear") );
            }
            else
            {
                xAttrs->AddAttribute( "draw:style", "ellipsoid" );
                xAttrs->AddAttribute( "draw:cx", "50%" );
                xAttrs->AddAttribute( "draw:cy", "50%" );
            }

            basegfx::B2DTuple rScale, rTranslate;
            double rRotate, rShearX;
            if( rState.maFillGradient.maTransform.decompose(rScale, rTranslate, rRotate, rShearX) )
                xAttrs->AddAttribute( "draw:angle",
                                      OUString::number(rRotate*1800.0/M_PI ) );
            xAttrs->AddAttribute( "draw:start-color",
                                  getOdfColor(
                                      maGradientStopVector[
                                          rState.maFillGradient.maStops[0]].maStopColor) );
            xAttrs->AddAttribute( "draw:end-color",
                                  getOdfColor(
                                      maGradientStopVector[
                                          rState.maFillGradient.maStops[1]].maStopColor) );
            xAttrs->AddAttribute( "draw:border", "0%" );
            mxDocumentHandler->startElement( "draw:gradient", xUnoAttrs );
            mxDocumentHandler->endElement( "draw:gradient" );

            if( hasGradientOpacity(rState.maFillGradient) )
            {
                // need to write out opacity style as well
                xAttrs->Clear();
                xAttrs->AddAttribute( "draw:name", getStyleName("svgopacity", rState.maFillGradient.mnId) );
                if( rState.maFillGradient.meType == Gradient::LINEAR )
                {
                    xAttrs->AddAttribute( "draw:style", "linear" );
                }
                else
                {
                    xAttrs->AddAttribute( "draw:style", "ellipsoid" );
                    xAttrs->AddAttribute( "draw:cx", "50%" );
                    xAttrs->AddAttribute( "draw:cy", "50%" );
                }

                // modulate gradient opacity with overall fill opacity
                xAttrs->AddAttribute( "draw:end",
                                      OUString::number(
                                          maGradientStopVector[
                                              rState.maFillGradient.maStops[0]].maStopColor.a*
                                          maCurrState.mnFillOpacity*maCurrState.mnOpacity*100.0)+"%" );
                xAttrs->AddAttribute( "draw:start",
                                      OUString::number(
                                          maGradientStopVector[
                                              rState.maFillGradient.maStops[1]].maStopColor.a*
                                          maCurrState.mnFillOpacity*maCurrState.mnOpacity*100.0)+"%" );
                xAttrs->AddAttribute( "draw:border", "0%" );
                mxDocumentHandler->startElement( "draw:opacity", xUnoAttrs );
                mxDocumentHandler->endElement( "draw:opacity" );
            }
        }

        // serialize to automatic-style section
        if( nTagId == XML_TEXT || nTagId == XML_TSPAN)
        {
            // write paragraph style attributes
            xAttrs->Clear();

            xAttrs->AddAttribute( "style:name", getStyleName("svgparagraphstyle", mnCurrStateId) );
            xAttrs->AddAttribute( "style:family", "paragraph" );
            mxDocumentHandler->startElement( "style:style", xUnoAttrs );

            xAttrs->Clear();
            xAttrs->AddAttribute( "fo:text-align", getOdfAlign(rState.meTextAnchor));

            mxDocumentHandler->startElement( "style:paragraph-properties", xUnoAttrs );
            mxDocumentHandler->endElement( "style:paragraph-properties" );
            mxDocumentHandler->endElement( "style:style" );

            // write text style attributes
            xAttrs->Clear();

            xAttrs->AddAttribute( "style:name", getStyleName("svgtextstyle", mnCurrStateId) );
            xAttrs->AddAttribute( "style:family", "text" );
            mxDocumentHandler->startElement( "style:style", xUnoAttrs );
            xAttrs->Clear();
            xAttrs->AddAttribute( "fo:font-family", rState.maFontFamily);
            xAttrs->AddAttribute( "fo:font-size",
                                  OUString::number(pt2mm(rState.mnFontSize))+"mm");
            xAttrs->AddAttribute( "fo:font-style", rState.maFontStyle);
            xAttrs->AddAttribute( "fo:font-variant", rState.maFontVariant);
            xAttrs->AddAttribute( "fo:font-weight",
                                  OUString::number(rState.mnFontWeight));
            xAttrs->AddAttribute( "fo:color", getOdfColor(rState.maFillColor));

            mxDocumentHandler->startElement( "style:text-properties", xUnoAttrs );
            mxDocumentHandler->endElement( "style:text-properties" );
            mxDocumentHandler->endElement( "style:style" );
        }

        xAttrs->Clear();
        xAttrs->AddAttribute( "style:name" , getStyleName("svggraphicstyle", mnCurrStateId) );
        xAttrs->AddAttribute( "style:family", "graphic" );
        mxDocumentHandler->startElement( "style:style", xUnoAttrs );

        xAttrs->Clear();
        // text or shape? if the former, no use in processing any
        // graphic attributes except stroke color, ODF can do ~nothing
        // with text shapes
        if( nTagId == XML_TEXT || nTagId == XML_TSPAN )
        {
            //xAttrs->AddAttribute( "draw:auto-grow-height", "true");
            xAttrs->AddAttribute( "draw:auto-grow-width", "true");
            xAttrs->AddAttribute( "draw:textarea-horizontal-align", "left");
            //xAttrs->AddAttribute( "draw:textarea-vertical-align", "top");
            xAttrs->AddAttribute( "fo:min-height", "0cm");

            xAttrs->AddAttribute( "fo:padding-top", "0cm");
            xAttrs->AddAttribute( "fo:padding-left", "0cm");
            xAttrs->AddAttribute( "fo:padding-right", "0cm");
            xAttrs->AddAttribute( "fo:padding-bottom", "0cm");

            // disable any background shape
            xAttrs->AddAttribute( "draw:stroke", "none");
            xAttrs->AddAttribute( "draw:fill", "none");
        }
        else
        {
            if( rState.meFillType != NONE )
            {
                if( rState.meFillType == GRADIENT )
                {
                    // don't fill the gradient if there's no stop element present
                    if( rState.maFillGradient.maStops.size() == 0 )
                        xAttrs->AddAttribute( "draw:fill", "none" );
                    else
                    {
                        xAttrs->AddAttribute( "draw:fill", "gradient");
                        xAttrs->AddAttribute( "draw:fill-gradient-name",
                                              getStyleName("svggradient", rState.maFillGradient.mnId) );
                        if( hasGradientOpacity(rState.maFillGradient) )
                        {
                            // needs transparency gradient as well
                            xAttrs->AddAttribute( "draw:opacity-name",
                                                  getStyleName("svgopacity", rState.maFillGradient.mnId) );
                        }
                        else if( maCurrState.mnFillOpacity*maCurrState.mnOpacity != 1.0 )
                            xAttrs->AddAttribute( "draw:opacity",
                                                  OUString::number(100.0*maCurrState.mnFillOpacity*maCurrState.mnOpacity)+"%" );
                    }
                }
                else
                {
                    xAttrs->AddAttribute( "draw:fill", "solid");
                    xAttrs->AddAttribute( "draw:fill-color", getOdfColor(rState.maFillColor));
                    if( maCurrState.mnFillOpacity*maCurrState.mnOpacity != 1.0 )
                        xAttrs->AddAttribute( "draw:opacity",
                                              OUString::number(100.0*maCurrState.mnFillOpacity*maCurrState.mnOpacity)+"%" );
                }
            }
            else
                xAttrs->AddAttribute( "draw:fill", "none");

            if( rState.meStrokeType == SOLID )
            {
                xAttrs->AddAttribute( "draw:stroke", "solid");
                xAttrs->AddAttribute( "svg:stroke-color", getOdfColor(rState.maStrokeColor));
            }
            else if( rState.meStrokeType == DASH )
            {
                xAttrs->AddAttribute( "draw:stroke", "dash");
                xAttrs->AddAttribute( "draw:stroke-dash", "dash"+OUString::number(mnCurrStateId));
                xAttrs->AddAttribute( "svg:stroke-color", getOdfColor(rState.maStrokeColor));
            }
            else
                xAttrs->AddAttribute( "draw:stroke", "none");

            if( maCurrState.mnStrokeWidth != 0.0 )
            {
                ::basegfx::B2DVector aVec(maCurrState.mnStrokeWidth,0);
                aVec *= maCurrState.maCTM;
                xAttrs->AddAttribute( "svg:stroke-width", OUString::number( pt2mm(aVec.getLength()) )+"mm");
            }
            if( maCurrState.meLineJoin == basegfx::B2DLineJoin::Miter )
                xAttrs->AddAttribute( "draw:stroke-linejoin", "miter");
            else if( maCurrState.meLineJoin == basegfx::B2DLineJoin::Round )
                xAttrs->AddAttribute( "draw:stroke-linejoin", "round");
            else if( maCurrState.meLineJoin == basegfx::B2DLineJoin::Bevel )
                xAttrs->AddAttribute( "draw:stroke-linejoin", "bevel");
            if( maCurrState.mnStrokeOpacity*maCurrState.mnOpacity != 1.0 )
                xAttrs->AddAttribute( "svg:stroke-opacity",
                                      OUString::number(100.0*maCurrState.mnStrokeOpacity*maCurrState.mnOpacity)+"%");
        }

        mxDocumentHandler->startElement( "style:graphic-properties", xUnoAttrs );
        mxDocumentHandler->endElement( "style:graphic-properties" );
        mxDocumentHandler->endElement( "style:style" );

        return true; // newly written
    }

    void writeStyle(const uno::Reference<xml::dom::XElement>& xElem, const sal_Int32 nTagId)
    {
        SAL_INFO ("filter.svg", "writeStyle xElem " << xElem->getTagName());

        sal_Int32 nStyleId=0;
        if( writeStyle(maCurrState, nTagId) )
            nStyleId = mnCurrStateId;
        else
            nStyleId = mrStates.find(maCurrState)->mnStyleId;

        xElem->setAttribute("internal-style-ref",
                            OUString::number(
                                nStyleId)
                            +"$0");
    }

    void push()
    {
        maParentStates.push_back(maCurrState);
    }

    void pop()
    {
        maParentStates.pop_back();
    }

    void parseLinearGradientData( Gradient& io_rCurrGradient,
                                  const sal_Int32 nGradientNumber,
                                  const sal_Int32 nTokenId,
                                  const OUString& sValue )
    {
        switch(nTokenId)
        {
            case XML_GRADIENTTRANSFORM:
            {
                OString aValueUtf8( sValue.getStr(),
                                         sValue.getLength(),
                                         RTL_TEXTENCODING_UTF8 );
                parseTransform(aValueUtf8.getStr(),io_rCurrGradient.maTransform);
                break;
            }
            case XML_X1:
                io_rCurrGradient.maCoords.linear.mfX1 = convLength(sValue,maCurrState,'h');
                break;
            case XML_X2:
                io_rCurrGradient.maCoords.linear.mfX2 = convLength(sValue,maCurrState,'h');
                break;
            case XML_Y1:
                io_rCurrGradient.maCoords.linear.mfY1 = convLength(sValue,maCurrState,'v');
                break;
            case XML_Y2:
                io_rCurrGradient.maCoords.linear.mfY2 = convLength(sValue,maCurrState,'v');
                break;
            case XML_ID:
                maGradientIdMap.insert(std::make_pair(sValue,nGradientNumber));
                break;
            case XML_GRADIENTUNITS:
                if (getTokenId(sValue) == XML_OBJECTBOUNDINGBOX)
                    io_rCurrGradient.mbBoundingBoxUnits = true;
                else
                    io_rCurrGradient.mbBoundingBoxUnits = false;
                break;
            default:
                break;
        }
    }

    void parseRadialGradientData( Gradient& io_rCurrGradient,
                                  const sal_Int32 nGradientNumber,
                                  const sal_Int32 nTokenId,
                                  const OUString& sValue )
    {
        switch(nTokenId)
        {
            case XML_GRADIENTTRANSFORM:
            {
                OString aValueUtf8( sValue.getStr(),
                                         sValue.getLength(),
                                         RTL_TEXTENCODING_UTF8 );
                parseTransform(aValueUtf8.getStr(),io_rCurrGradient.maTransform);
                break;
            }
            case XML_CX:
                io_rCurrGradient.maCoords.radial.mfCX = convLength(sValue,maCurrState,'h');
                break;
            case XML_CY:
                io_rCurrGradient.maCoords.radial.mfCY = convLength(sValue,maCurrState,'v');
                break;
            case XML_FX:
                io_rCurrGradient.maCoords.radial.mfFX = convLength(sValue,maCurrState,'h');
                break;
            case XML_FY:
                io_rCurrGradient.maCoords.radial.mfFY = convLength(sValue,maCurrState,'v');
                break;
            case XML_R:
                io_rCurrGradient.maCoords.radial.mfR = convLength(sValue,maCurrState,'r');
                break;
            case XML_ID:
                maGradientIdMap.insert(std::make_pair(sValue,nGradientNumber));
                break;
            case XML_GRADIENTUNITS:
                if (getTokenId(sValue) == XML_OBJECTBOUNDINGBOX)
                    io_rCurrGradient.mbBoundingBoxUnits = true;
                else
                    io_rCurrGradient.mbBoundingBoxUnits = false;
                break;
            default:
                break;
        }
    }

    void parseGradientStop( GradientStop& io_rGradientStop,
                            const sal_Int32 nStopNumber,
                            const sal_Int32 nTokenId,
                            const OUString& sValue )
    {
        switch(nTokenId)
        {
            case XML_HREF:
            {
                ElementRefMapType::iterator aFound=maStopIdMap.end();
                if ( sValue.startsWith("#") )
                    aFound = maStopIdMap.find(sValue.copy(1));
                else
                    aFound = maStopIdMap.find(sValue);

                if( aFound != maStopIdMap.end() )
                    io_rGradientStop =  maGradientStopVector[aFound->second];
                break;
            }
            case XML_ID:
                maStopIdMap.insert(std::make_pair(sValue,nStopNumber));
                break;
            case XML_STOP_COLOR:
                if( maGradientVector.empty() ||
                    maGradientVector.back().maStops.empty() )
                    break;
                parseColor( sValue.toUtf8().getStr(), maGradientStopVector[
                                  maGradientVector.back().maStops.back()].maStopColor );
                break;
            case XML_STOP_OPACITY:
                if( maGradientVector.empty() ||
                    maGradientVector.back().maStops.empty() )
                    break;
                parseOpacity( sValue.toUtf8().getStr(),
                              maGradientStopVector[
                                  maGradientVector.back().maStops.back()].maStopColor );
                break;
            case XML_OFFSET:
                io_rGradientStop.mnStopPosition = sValue.toDouble();
                break;
            case XML_STYLE:
                parseStyle( sValue );
                break;
            default:
                break;
        }
    }

    void parseAttribute( const sal_Int32      nTokenId,
                         const OUString& sValue )
    {
        OString aValueUtf8( sValue.getStr(),
                                 sValue.getLength(),
                                 RTL_TEXTENCODING_UTF8 );
        switch(nTokenId)
        {
            case XML_WIDTH:
            {
                const double fViewPortWidth(
                    convLength(sValue,maCurrState,'h'));

                maCurrState.maViewport.expand(
                    basegfx::B2DTuple(fViewPortWidth,0.0));
                break;
            }
            case XML_HEIGHT:
            {
                const double fViewPortHeight(
                    convLength(sValue,maCurrState,'v'));

                maCurrState.maViewport.expand(
                    basegfx::B2DTuple(0.0,fViewPortHeight));
                break;
            }
            case XML_VIEWBOX:
            {
                // TODO(F1): preserveAspectRatio
                parseViewBox(
                    aValueUtf8.getStr(),
                    maCurrState.maViewBox);
                break;
            }
            case XML_FILL_RULE:
            {
                if( aValueUtf8 == "evenodd" )
                    maCurrState.meFillRule = EVEN_ODD;
                else if( aValueUtf8 == "nonzero" )
                    maCurrState.meFillRule = NON_ZERO;
                else if( aValueUtf8 == "inherit" )
                    maCurrState.meFillRule = maParentStates.back().meFillRule;
                break;
            }
            case XML_OPACITY:
                if( aValueUtf8 == "inherit" )
                    maCurrState.mnOpacity = maParentStates.back().mnOpacity;
                else
                    maCurrState.mnOpacity = aValueUtf8.toDouble();
                break;
            case XML_FILL_OPACITY:
                if( aValueUtf8 == "inherit" )
                    maCurrState.mnFillOpacity = maParentStates.back().mnFillOpacity;
                else {
                    maCurrState.mnFillOpacity = aValueUtf8.toDouble();
                    if( maCurrState.mnFillOpacity > 1 )
                        maCurrState.mnFillOpacity = 1;
                }
                break;
            case XML_STROKE_WIDTH:
            {
                if( aValueUtf8 == "inherit" )
                    maCurrState.mnStrokeWidth = maParentStates.back().mnStrokeWidth;
                else
                    maCurrState.mnStrokeWidth = convLength(sValue,maCurrState,'r');
                break;
            }
            case XML_STROKE_LINECAP:
            {
                if( aValueUtf8 == "butt" )
                    maCurrState.meLineCap = BUTT;
                else if( aValueUtf8 == "round" )
                    maCurrState.meLineCap = ROUND;
                else if( aValueUtf8 == "square" )
                    maCurrState.meLineCap = RECT;
                else if( aValueUtf8 == "inherit" )
                    maCurrState.meLineCap = maParentStates.back().meLineCap;
                break;
            }
            case XML_STROKE_LINEJOIN:
            {
                if( aValueUtf8 == "miter" )
                    maCurrState.meLineJoin = basegfx::B2DLineJoin::Miter;
                else if( aValueUtf8 == "round" )
                    maCurrState.meLineJoin = basegfx::B2DLineJoin::Round;
                else if( aValueUtf8 == "bevel" )
                    maCurrState.meLineJoin = basegfx::B2DLineJoin::Bevel;
                else if( aValueUtf8 == "inherit" )
                    maCurrState.meLineJoin = maParentStates.back().meLineJoin;
                break;
            }
            case XML_STROKE_MITERLIMIT:
            {
                if( aValueUtf8 == "inherit" )
                    maCurrState.mnMiterLimit = maParentStates.back().mnMiterLimit;
                else
                    maCurrState.mnMiterLimit = aValueUtf8.toDouble();
                break;
            }
            case XML_STROKE_DASHOFFSET:
            {
                if( aValueUtf8 == "inherit" )
                    maCurrState.mnDashOffset = maParentStates.back().mnDashOffset;
                else
                    maCurrState.mnDashOffset = convLength(sValue,maCurrState,'r');
                break;
            }
            case XML_STROKE_DASHARRAY:
            {
                if( aValueUtf8 == "none" )
                {
                    maCurrState.maDashArray.clear();
                    maCurrState.meStrokeType = SOLID;
                }
                else if( aValueUtf8 == "inherit" )
                    maCurrState.maDashArray = maParentStates.back().maDashArray;
                else
                {
                    if( parseDashArray(aValueUtf8.getStr(),
                                      maCurrState.maDashArray) )
                    {
                        maCurrState.meStrokeType = DASH;
                    }
                    else
                    {
                        maCurrState.meStrokeType = SOLID;
                    }
                }
                break;
            }
            case XML_STROKE_OPACITY:
                if( aValueUtf8 == "inherit" )
                    maCurrState.mnStrokeOpacity = maParentStates.back().mnStrokeOpacity;
                else
                    maCurrState.mnStrokeOpacity = aValueUtf8.toDouble();
                break;
            case XML_FILL:
            {
                const State& rParent( maParentStates.back() );
                parsePaint( sValue,
                            aValueUtf8.getStr(),
                            maCurrState.meFillType,
                            maCurrState.maFillColor,
                            maCurrState.maFillGradient,
                            rParent.meFillType,
                            rParent.maFillColor,
                            rParent.maFillGradient );
                break;
            }
            case XML_STROKE:
            {
                const State& rParent( maParentStates.back() );
                parsePaint( sValue,
                            aValueUtf8.getStr(),
                            maCurrState.meStrokeType,
                            maCurrState.maStrokeColor,
                            maCurrState.maStrokeGradient,
                            rParent.meStrokeType,
                            rParent.maStrokeColor,
                            rParent.maStrokeGradient );
                break;
            }
            case XML_COLOR:
            {
                if( aValueUtf8 == "inherit" )
                    maCurrState.maCurrentColor = maParentStates.back().maCurrentColor;
                else
                    parseColor(aValueUtf8.getStr(), maCurrState.maCurrentColor);
                break;
            }
            case XML_TRANSFORM:
            {
                basegfx::B2DHomMatrix aTransform;
                parseTransform(aValueUtf8.getStr(),aTransform);
                maCurrState.maTransform = maCurrState.maTransform*aTransform;
                break;
            }
            case XML_FONT_FAMILY:
                maCurrState.maFontFamily=sValue;
                break;
            case XML_FONT_SIZE:
                maCurrState.mnParentFontSize=maParentStates.back().mnFontSize;
                maCurrState.mnFontSize=convLength(sValue,maCurrState,'v');
                break;
            case XML_FONT_STYLE:
                parseFontStyle(maCurrState,sValue,aValueUtf8.getStr());
                break;
            case XML_FONT_WEIGHT:
                maCurrState.mnFontWeight=sValue.toDouble();
                break;
            case XML_FONT_VARIANT:
                parseFontVariant(maCurrState,sValue,aValueUtf8.getStr());
                break;
            case XML_TEXT_ANCHOR:
                parseTextAlign(maCurrState,aValueUtf8.getStr());
                break;
            case XML_STOP_COLOR:
            case XML_STOP_OPACITY:
                parseGradientStop( maGradientStopVector.back(),
                                   maGradientStopVector.size()-1,
                                   nTokenId, sValue );
                break;
            case XML_TOKEN_INVALID:
                SAL_INFO("filter.svg", "unhandled token");
                break;
            default:
                SAL_INFO("filter.svg", "unhandled token " << getTokenName(nTokenId));
                break;
        }
    }

    void parseStyle( const OUString& sValue )
    {
        // split individual style attributes
        sal_Int32 nIndex=0, nDummyIndex=0;
        OUString aCurrToken;
        do
        {
            aCurrToken=sValue.getToken(0,';',nIndex);

            if( !aCurrToken.isEmpty() )
            {
                // split attrib & value
                nDummyIndex=0;
                OUString aCurrAttrib(
                    aCurrToken.getToken(0,':',nDummyIndex).trim());
                OSL_ASSERT(nDummyIndex!=-1);
                nDummyIndex=0;
                OUString aCurrValue(
                    aCurrToken.getToken(1,':',nDummyIndex).trim());
                OSL_ASSERT(nDummyIndex==-1);

                // recurse into normal attribute parsing
                parseAttribute( getTokenId(aCurrAttrib),
                                aCurrValue );
            }
        }
        while( nIndex != -1 );
    }

    static void parseFontStyle( State&               io_rInitialState,
                         const OUString& rValue,
                         const char*          sValue )
    {
        if( strcmp(sValue,"inherit") != 0 )
            io_rInitialState.maFontStyle = rValue;
    }

    static void parseFontVariant( State&               io_rInitialState,
                           const OUString& rValue,
                           const char*          sValue )
    {
        if( strcmp(sValue,"inherit") != 0 )
            io_rInitialState.maFontVariant = rValue;
    }

    static void parseTextAlign( State&      io_rInitialState,
                         const char* sValue )
    {
        if( strcmp(sValue,"start") == 0 )
            io_rInitialState.meTextAnchor = BEFORE;
        else if( strcmp(sValue,"middle") == 0 )
            io_rInitialState.meTextAnchor = CENTER;
        else if( strcmp(sValue,"end") == 0 )
            io_rInitialState.meTextAnchor = AFTER;
        // keep current val for sValue == "inherit"
    }

    void parsePaint( const OUString& rValue,
                     const char*      sValue,
                     PaintType&       rType,
                     ARGBColor&       rColor,
                     Gradient&        rGradient,
                     const PaintType& rInheritType,
                     const ARGBColor& rInheritColor,
                     const Gradient&  rInheritGradient )
    {
        std::pair<const char*,const char*> aPaintUri(nullptr,nullptr);
        std::pair<ARGBColor,bool>          aColor(maCurrState.maCurrentColor,
                                                  false);
        if( strcmp(sValue,"none") == 0 )
            rType = NONE;
        else if( strcmp(sValue,"currentColor") == 0 )
        {
            rType = SOLID;
            rColor = maCurrState.maCurrentColor;
        }
        else if( strcmp(sValue,"inherit") == 0)
        {
            rType = rInheritType;
            rColor = rInheritColor;
            rGradient = rInheritGradient;
        }
        else if( parsePaintUri(aPaintUri,aColor,sValue) )
        {
            if( aPaintUri.first != aPaintUri.second )
            {
                // assuming gradient. assumption does not hold generally
                if( strstr(sValue,")") && rValue.getLength() > 5 )
                {
                    ElementRefMapType::iterator aRes;
                    if( (aRes=maGradientIdMap.find(
                             rValue.copy(aPaintUri.first-sValue,
                                         aPaintUri.second-aPaintUri.first))) != maGradientIdMap.end() )
                    {
                        rGradient = maGradientVector[aRes->second];
                        rType = GRADIENT;
                    }
                }
            }
            else if( aColor.second )
            {
                rType = SOLID;
                rColor = aColor.first;
            }
            else
            {
                rType = NONE;
            }
        }
        else
        {
            rType = SOLID;
            parseColor(sValue,rColor);
        }
    }

    sal_Int32                                  mnCurrStateId;
    State                                      maCurrState;
    std::vector<State>                         maParentStates;
    StatePool&                                 mrStates;
    StateMap&                                  mrStateMap;
    uno::Reference<xml::sax::XDocumentHandler> mxDocumentHandler;
    std::vector< Gradient >                    maGradientVector;
    std::vector< GradientStop >                maGradientStopVector;
    std::vector< uno::Reference<xml::dom::XElement> > maElementVector;
    std::vector< uno::Reference<xml::dom::XElement> >& mrUseElementVector;
    ElementRefMapType                          maGradientIdMap;
    ElementRefMapType                          maStopIdMap;
    ElementRefMapType                          maElementIdMap;
    bool&                                      mrGradientNotFound;
};

/// Annotate svg styles with unique references to state pool
void annotateStyles( StatePool&                                        rStatePool,
                            StateMap&                                         rStateMap,
                            const State&                                       rInitialState,
                            uno::Reference<xml::dom::XElement> const &         rElem,
                            const uno::Reference<xml::sax::XDocumentHandler>& xDocHdl,
                            std::vector< uno::Reference<xml::dom::XElement> >& rUseElementVector )
{
    bool bGradientNotFound = false;
    AnnotatingVisitor aVisitor(rStatePool, rStateMap, rInitialState, xDocHdl, rUseElementVector, bGradientNotFound);
    visitElements(aVisitor, rElem, STYLE_ANNOTATOR);

    //Sometimes, xlink:href in gradients refers to another gradient which hasn't been parsed yet.
    // if that happens, we'll need to parse the styles again, so everything gets referred.
    if( bGradientNotFound )
    {
        visitElements(aVisitor, rElem, STYLE_ANNOTATOR);
    }
}

struct ShapeWritingVisitor
{
    ShapeWritingVisitor(StateMap&                                         rStateMap,
                        const uno::Reference<xml::sax::XDocumentHandler>& xDocumentHandler) :
        mrStateMap(rStateMap),
        mxDocumentHandler(xDocumentHandler),
        mnShapeNum(0)
    {}

    void operator()( const uno::Reference<xml::dom::XElement>& )
    {
    }

    void operator()( const uno::Reference<xml::dom::XElement>&      xElem,
                     const uno::Reference<xml::dom::XNamedNodeMap>& xAttributes )
    {
        rtl::Reference<SvXMLAttributeList> xAttrs( new SvXMLAttributeList() );
        uno::Reference<xml::sax::XAttributeList> xUnoAttrs( xAttrs.get() );

        sal_Int32 nDummyIndex(0);
        OUString sStyleId(
            xElem->getAttribute("internal-style-ref").getToken(
                    0,'$',nDummyIndex));
        StateMap::iterator pOrigState=mrStateMap.find(
            sStyleId.toInt32());

        if( pOrigState == mrStateMap.end() )
            return; // non-exportable element, e.g. linearGradient

        maCurrState = pOrigState->second;

        const sal_Int32 nTokenId(getTokenId(xElem->getNodeName()));
        switch(nTokenId)
        {
            case XML_LINE:
            {
                // collect attributes
                const sal_Int32 nNumAttrs( xAttributes->getLength() );
                OUString sAttributeValue;
                double x1=0.0,y1=0.0,x2=0.0,y2=0.0;
                for( sal_Int32 i=0; i<nNumAttrs; ++i )
                {
                    sAttributeValue = xAttributes->item(i)->getNodeValue();
                    const sal_Int32 nAttribId(
                        getTokenId(xAttributes->item(i)->getNodeName()));
                    switch(nAttribId)
                    {
                        case XML_X1:
                            x1= convLength(sAttributeValue,maCurrState,'h');
                            break;
                        case XML_X2:
                            x2 = convLength(sAttributeValue,maCurrState,'h');
                            break;
                        case XML_Y1:
                            y1 = convLength(sAttributeValue,maCurrState,'v');
                            break;
                        case XML_Y2:
                            y2 = convLength(sAttributeValue,maCurrState,'v');
                            break;
                        default:
                            // skip
                            break;
                    }
                }

                if ( x1 != x2 || y1 != y2 ) {
                    OUString sLinePath = "M"+OUString::number(x1)+","
                        +OUString::number(y1)+"L"+OUString::number(x2)+","
                        +OUString::number(y2);
                    basegfx::B2DPolyPolygon aPoly;
                    basegfx::utils::importFromSvgD(aPoly, sLinePath, false, nullptr);

                    writePathShape(xAttrs,
                                   xUnoAttrs,
                                   sStyleId,
                                   aPoly);
                }

                break;
            }
            case XML_POLYGON:
            case XML_POLYLINE:
            {
                OUString sPoints = xElem->hasAttribute("points") ? xElem->getAttribute("points") : "";
                basegfx::B2DPolygon aPoly;
                (void)basegfx::utils::importFromSvgPoints(aPoly, sPoints);
                if( nTokenId == XML_POLYGON || maCurrState.meFillType != NONE )
                    aPoly.setClosed(true);

                writePathShape(xAttrs,
                               xUnoAttrs,
                               sStyleId,
                               basegfx::B2DPolyPolygon(aPoly));
                break;
            }
            case XML_RECT:
            {
                // collect attributes
                const sal_Int32 nNumAttrs( xAttributes->getLength() );
                OUString sAttributeValue;
                bool bRxSeen=false, bRySeen=false;
                double x=0.0,y=0.0,width=0.0,height=0.0,rx=0.0,ry=0.0;
                for( sal_Int32 i=0; i<nNumAttrs; ++i )
                {
                    sAttributeValue = xAttributes->item(i)->getNodeValue();
                    const sal_Int32 nAttribId(
                                              getTokenId(xAttributes->item(i)->getNodeName()));
                    switch(nAttribId)
                    {
                    case XML_X:
                        x = convLength(sAttributeValue,maCurrState,'h');
                        break;
                    case XML_Y:
                        y = convLength(sAttributeValue,maCurrState,'v');
                        break;
                    case XML_WIDTH:
                        width = convLength(sAttributeValue,maCurrState,'h');
                        break;
                    case XML_HEIGHT:
                        height = convLength(sAttributeValue,maCurrState,'v');
                        break;
                    case XML_RX:
                        rx = convLength(sAttributeValue,maCurrState,'h');
                        bRxSeen=true;
                        break;
                    case XML_RY:
                        ry = convLength(sAttributeValue,maCurrState,'v');
                        bRySeen=true;
                        break;
                    default:
                        // skip
                        break;
                    }
                }

                if ( (width > 0) && (height > 0) ) {
                    if( bRxSeen && !bRySeen )
                        ry = rx;
                    else if( bRySeen && !bRxSeen )
                        rx = ry;

                    basegfx::B2DPolygon aPoly;
                    aPoly = basegfx::utils::createPolygonFromRect(
                                                                  basegfx::B2DRange(x,y,x+width,y+height),
                                                                  rx/(0.5*width), ry/(0.5*height) );

                    writePathShape(xAttrs,
                                   xUnoAttrs,
                                   sStyleId,
                                   basegfx::B2DPolyPolygon(aPoly));
                }
                break;
            }
            case XML_PATH:
            {
                OUString sPath = xElem->hasAttribute("d") ? xElem->getAttribute("d") : "";
                basegfx::B2DPolyPolygon aPoly;
                basegfx::utils::importFromSvgD(aPoly, sPath, false, nullptr);

                if ((maCurrState.meStrokeType == NONE) &&
                    (maCurrState.meFillType != NONE) &&
                    !aPoly.isClosed())
                {
                    aPoly.setClosed(true);
                }

                // tdf#51165: rendering of paths with open and closed polygons is not implemented
                // split mixed polypolygons into single polygons and add them one by one
                if( PolyPolygonIsMixedOpenAndClosed(aPoly) )
                {
                    for( sal_uInt32 i(0); i<aPoly.count(); ++i ) {
                        writePathShape(xAttrs,
                                       xUnoAttrs,
                                       sStyleId,
                                       basegfx::B2DPolyPolygon(aPoly.getB2DPolygon(i)));
                    }
                }
                else
                {
                    writePathShape(xAttrs,
                                   xUnoAttrs,
                                   sStyleId,
                                   aPoly);
                }
                break;
            }
            case XML_CIRCLE:
            {
                // collect attributes
                const sal_Int32 nNumAttrs( xAttributes->getLength() );
                OUString sAttributeValue;
                double cx=0.0,cy=0.0,r=0.0;
                for( sal_Int32 i=0; i<nNumAttrs; ++i )
                {
                    sAttributeValue = xAttributes->item(i)->getNodeValue();
                    const sal_Int32 nAttribId(
                        getTokenId(xAttributes->item(i)->getNodeName()));
                    switch(nAttribId)
                    {
                        case XML_CX:
                            cx = convLength(sAttributeValue,maCurrState,'h');
                            break;
                        case XML_CY:
                            cy = convLength(sAttributeValue,maCurrState,'v');
                            break;
                        case XML_R:
                            r = convLength(sAttributeValue,maCurrState,'r');
                            break;
                        default:
                            // skip
                            break;
                    }
                }

                if ( r > 0 )
                    writeEllipseShape(xAttrs,
                                      xUnoAttrs,
                                      sStyleId,
                                      basegfx::B2DEllipse(basegfx::B2DPoint(cx, cy), basegfx::B2DTuple(r,r)));
                break;
            }
            case XML_ELLIPSE:
            {
                // collect attributes
                const sal_Int32 nNumAttrs( xAttributes->getLength() );
                OUString sAttributeValue;
                double cx=0.0,cy=0.0,rx=0.0, ry=0.0;
                for( sal_Int32 i=0; i<nNumAttrs; ++i )
                {
                    sAttributeValue = xAttributes->item(i)->getNodeValue();
                    const sal_Int32 nAttribId(
                        getTokenId(xAttributes->item(i)->getNodeName()));
                    switch(nAttribId)
                    {
                        case XML_CX:
                            cx = convLength(sAttributeValue,maCurrState,'h');
                            break;
                        case XML_CY:
                            cy = convLength(sAttributeValue,maCurrState,'v');
                            break;
                        case XML_RX:
                            rx = convLength(sAttributeValue,maCurrState,'h');
                            break;
                        case XML_RY:
                            ry = convLength(sAttributeValue,maCurrState,'v');
                            break;
                        default:
                            // skip
                            break;
                    }
                }

                if ( rx > 0 && ry > 0 )
                    writeEllipseShape(xAttrs,
                                      xUnoAttrs,
                                      sStyleId,
                                      basegfx::B2DEllipse(basegfx::B2DPoint(cx, cy), basegfx::B2DTuple(rx,ry)));
                break;
            }
            case XML_IMAGE:
            {
                // collect attributes
                const sal_Int32 nNumAttrs( xAttributes->getLength() );
                OUString sAttributeValue;
                double x=0.0, y=0.0, width=0.0, height=0.0;
                for( sal_Int32 i=0; i<nNumAttrs; ++i )
                {
                    sAttributeValue = xAttributes->item(i)->getNodeValue();
                    const sal_Int32 nAttribId(
                        getTokenId(xAttributes->item(i)->getNodeName()));
                    switch(nAttribId)
                    {
                        case XML_X:
                            x = convLength(sAttributeValue,maCurrState,'h');
                            break;
                        case XML_Y:
                            y = convLength(sAttributeValue,maCurrState,'v');
                            break;
                        case XML_WIDTH:
                            width = convLength(sAttributeValue,maCurrState,'h');
                            break;
                        case XML_HEIGHT:
                            height = convLength(sAttributeValue,maCurrState,'v');
                            break;
                        default:
                            // skip
                            break;
                    }
                }
                // extract basic transformations out of CTM
                basegfx::B2DTuple aScale, aTranslate;
                double fRotate, fShearX;
                if (maCurrState.maCTM.decompose(aScale, aTranslate, fRotate, fShearX))
                {
                    // apply transform
                    x *= aScale.getX();
                    width *= aScale.getX();
                    y *= aScale.getY();
                    height *= aScale.getY();
                    x += aTranslate.getX();
                    y += aTranslate.getY();
                    //TODO: Rotate
                }

                OUString sValue = xElem->hasAttribute("href") ? xElem->getAttribute("href") : "";
                OString aValueUtf8( sValue.getStr(), sValue.getLength(), RTL_TEXTENCODING_UTF8 );
                OUString sLinkValue;
                parseXlinkHref(aValueUtf8.getStr(), sLinkValue);

                if (!sLinkValue.isEmpty())
                    writeBinaryData(xAttrs, xUnoAttrs, basegfx::B2DRange(x,y,x+width,y+height), sLinkValue);
                break;
            }
            case XML_TSPAN:
            case XML_TEXT:
            {
                // collect text from all TEXT_NODE children into sText
                OUStringBuffer sText;
                visitChildren(
                    [&sText] (xml::dom::XNode & rNode) {
                        return sText.append(rNode.getNodeValue());
                    },
                              xElem,
                              xml::dom::NodeType_TEXT_NODE);

                // collect attributes
                const sal_Int32 nNumAttrs( xAttributes->getLength() );
                OUString sAttributeValue;
                double x=0.0,y=0.0;
                for( sal_Int32 i=0; i<nNumAttrs; ++i )
                {
                    sAttributeValue = xAttributes->item(i)->getNodeValue();
                    const sal_Int32 nAttribId(
                        getTokenId(xAttributes->item(i)->getNodeName()));
                    switch(nAttribId)
                    {
                        case XML_X:
                            x = convLength(sAttributeValue,maCurrState,'h');
                            break;
                        case XML_Y:
                            y = convLength(sAttributeValue,maCurrState,'v');
                            break;
                        default:
                            // skip
                            break;
                    }
                }

                // actually export text
                xAttrs->Clear();


                // extract basic transformations out of CTM
                basegfx::B2DTuple aScale, aTranslate;
                double fRotate, fShearX;
                if (maCurrState.maCTM.decompose(aScale, aTranslate, fRotate, fShearX))
                {
                    // some heuristic attempts to have text output
                    // baseline-relative
                    y -= 2.0*maCurrState.mnFontSize/aScale.getX()/3.0;
                    // apply transform
                    x *= aScale.getX();
                    y *= aScale.getY();
                    x += aTranslate.getX();
                    y += aTranslate.getY();
                    //TODO: Rotate
                }
                else {
                    // some heuristic attempts to have text output
                    // baseline-relative
                    y -= 2.0*maCurrState.mnFontSize/3.0;
                }

                xAttrs->AddAttribute( "svg:x", OUString::number(pt2mm(x))+"mm");
                xAttrs->AddAttribute( "svg:y", OUString::number(pt2mm(y))+"mm");
                xAttrs->AddAttribute( "draw:style-name", "svggraphicstyle"+sStyleId );

                mxDocumentHandler->startElement("draw:frame", xUnoAttrs);

                xAttrs->Clear();
                mxDocumentHandler->startElement("draw:text-box", xUnoAttrs);
                xAttrs->AddAttribute( "text:style-name", "svgparagraphstyle"+sStyleId);
                mxDocumentHandler->startElement("text:p", xUnoAttrs);

                xAttrs->Clear();
                xAttrs->AddAttribute( "text:style-name", "svgtextstyle"+sStyleId);
                mxDocumentHandler->startElement("text:span", xUnoAttrs);

                xAttrs->Clear();
                mxDocumentHandler->characters(sText.makeStringAndClear());
                mxDocumentHandler->endElement("text:span");
                mxDocumentHandler->endElement("text:p");
                mxDocumentHandler->endElement("draw:text-box");
                mxDocumentHandler->endElement("draw:frame");
                break;
            }
        }
    }

    static void push()
    {}

    static void pop()
    {}

    void writeBinaryData( rtl::Reference<SvXMLAttributeList> const &    xAttrs,
                        const uno::Reference<xml::sax::XAttributeList>& xUnoAttrs,
                        const basegfx::B2DRange&                        rShapeBounds,
                        const OUString&                                 data)
    {
        xAttrs->Clear();
        xAttrs->AddAttribute( "svg:x", OUString::number(pt2mm(rShapeBounds.getMinX()))+"mm");
        xAttrs->AddAttribute( "svg:y", OUString::number(pt2mm(rShapeBounds.getMinY()))+"mm");
        xAttrs->AddAttribute( "svg:width", OUString::number(pt2mm(rShapeBounds.getWidth()))+"mm");
        xAttrs->AddAttribute( "svg:height", OUString::number(pt2mm(rShapeBounds.getHeight()))+"mm");

        mxDocumentHandler->startElement("draw:frame", xUnoAttrs);

        xAttrs->Clear();
        mxDocumentHandler->startElement("draw:image", xUnoAttrs);

        mxDocumentHandler->startElement("office:binary-data", xUnoAttrs);

        mxDocumentHandler->characters(data);

        mxDocumentHandler->endElement("office:binary-data");

        mxDocumentHandler->endElement("draw:image");

        mxDocumentHandler->endElement("draw:frame");
    }


    void writeEllipseShape( rtl::Reference<SvXMLAttributeList> const &   xAttrs,
                         const uno::Reference<xml::sax::XAttributeList>& xUnoAttrs,
                         const OUString&                            rStyleId,
                         const basegfx::B2DEllipse&                      rEllipse)
    {
        State aState = maCurrState;

        xAttrs->Clear();

        basegfx::B2DPolygon aPoly = basegfx::utils::createPolygonFromEllipse(rEllipse.getB2DEllipseCenter(),
            rEllipse.getB2DEllipseRadius().getX(), rEllipse.getB2DEllipseRadius().getY());
        writePathShape(xAttrs, xUnoAttrs, rStyleId, basegfx::B2DPolyPolygon(aPoly));
    }

    void writePathShape( rtl::Reference<SvXMLAttributeList> const &      xAttrs,
                         const uno::Reference<xml::sax::XAttributeList>& xUnoAttrs,
                         const OUString&                                 rStyleId,
                         const basegfx::B2DPolyPolygon&                  rPoly )
    {
        // we might need to split up polypolygon into multiple path
        // shapes (e.g. when emulating line stroking)
        std::vector<basegfx::B2DPolyPolygon> aPolys(1,rPoly);
        State aState = maCurrState;

        xAttrs->Clear();

        // TODO(F2): separate out shear, rotate etc.
        // apply transformation to polygon, to keep draw
        // import in 100th mm
        for (basegfx::B2DPolyPolygon & aPoly : aPolys)
        {
            aPoly.transform(aState.maCTM);
        }

        for(basegfx::B2DPolyPolygon & aPoly : aPolys)
        {
            const basegfx::B2DRange aBounds(
                aPoly.areControlPointsUsed() ?
                basegfx::utils::getRange(
                    basegfx::utils::adaptiveSubdivideByAngle(aPoly)) :
                basegfx::utils::getRange(aPoly));
            fillShapeProperties(xAttrs,
                                aBounds,
                                "svggraphicstyle"+rStyleId);

            // force path coordinates to 100th millimeter, after
            // putting polygon data at origin (ODF viewbox
            // calculations largely untested codepaths, as OOo always
            // writes "0 0 w h" viewboxes)
            basegfx::B2DHomMatrix aNormalize;
            aNormalize.translate(-aBounds.getMinX(),-aBounds.getMinY());
            aNormalize.scale(2540.0/72.0,2540.0/72.0);
            aPoly.transform(aNormalize);

            xAttrs->AddAttribute( "svg:d", basegfx::utils::exportToSvgD(
                aPoly,
                false,   // no relative coords. causes rounding errors
                false,   // no quad bezier detection. crashes older versions.
                false ));
            mxDocumentHandler->startElement("draw:path", xUnoAttrs);
            mxDocumentHandler->endElement("draw:path");
        }
    }

    void fillShapeProperties( rtl::Reference<SvXMLAttributeList> const & xAttrs,
                              const basegfx::B2DRange&                  rShapeBounds,
                              const OUString&                      rStyleName )
    {
        xAttrs->AddAttribute( "draw:z-index", OUString::number( mnShapeNum++ ));
        xAttrs->AddAttribute( "draw:style-name", rStyleName);
        xAttrs->AddAttribute( "svg:width", OUString::number(pt2mm(rShapeBounds.getWidth()))+"mm");
        xAttrs->AddAttribute( "svg:height", OUString::number(pt2mm(rShapeBounds.getHeight()))+"mm");

        // OOo expects the viewbox to be in 100th of mm
        xAttrs->AddAttribute( "svg:viewBox",
            "0 0 "
            + OUString::number(
                basegfx::fround(pt100thmm(rShapeBounds.getWidth())) )
            + " "
            + OUString::number(
                basegfx::fround(pt100thmm(rShapeBounds.getHeight())) ));

        // TODO(F1): decompose transformation in calling code, and use
        // transform attribute here
        // writeTranslate(maCurrState.maCTM, xAttrs);
        xAttrs->AddAttribute( "svg:x", OUString::number(pt2mm(rShapeBounds.getMinX()))+"mm");
        xAttrs->AddAttribute( "svg:y", OUString::number(pt2mm(rShapeBounds.getMinY()))+"mm");
    }

    State                                      maCurrState;
    StateMap&                                  mrStateMap;
    uno::Reference<xml::sax::XDocumentHandler> mxDocumentHandler;
    sal_Int32                                  mnShapeNum;
};

/// Write out shapes from DOM tree
void writeShapes( StateMap&                                         rStateMap,
                  const uno::Reference<xml::dom::XElement>&         rElem,
                  const uno::Reference<xml::sax::XDocumentHandler>& xDocHdl,
                  std::vector< uno::Reference<xml::dom::XElement> >& rUseElementVector )
{
    ShapeWritingVisitor aVisitor(rStateMap,xDocHdl);
    visitElements(aVisitor, rElem, SHAPE_WRITER);

    for (auto const& useElement : rUseElementVector)
    {
        visitElements(aVisitor, useElement, SHAPE_WRITER);
    }
}

} // namespace

struct OfficeStylesWritingVisitor
{
    OfficeStylesWritingVisitor( StateMap&                                         rStateMap,
                                const uno::Reference<xml::sax::XDocumentHandler>& xDocumentHandler) :
        mrStateMap(rStateMap),
        mxDocumentHandler(xDocumentHandler)
    {}
    void operator()( const uno::Reference<xml::dom::XElement>& /*xElem*/ )
    {
    }
    void operator()( const uno::Reference<xml::dom::XElement>&      xElem,
                     const uno::Reference<xml::dom::XNamedNodeMap>& /*xAttributes*/ )
    {
        rtl::Reference<SvXMLAttributeList> xAttrs( new SvXMLAttributeList() );
        uno::Reference<xml::sax::XAttributeList> xUnoAttrs( xAttrs.get() );

        sal_Int32 nDummyIndex(0);
        OUString sStyleId(
            xElem->getAttribute("internal-style-ref").getToken(
                    0,'$',nDummyIndex));
        StateMap::iterator pOrigState=mrStateMap.find(
            sStyleId.toInt32());

        if( pOrigState == mrStateMap.end() )
            return; // non-exportable element, e.g. linearGradient

        maCurrState = pOrigState->second;

        if( maCurrState.meStrokeType == DASH )
        {
            sal_Int32 dots1, dots2;
            double dots1_length, dots2_length, dash_distance;
            SvgDashArray2Odf( &dots1, &dots1_length, &dots2, &dots2_length, &dash_distance );

            xAttrs->Clear();
            xAttrs->AddAttribute( "draw:name", "dash"+sStyleId );
            xAttrs->AddAttribute( "draw:display-name", "dash"+sStyleId );
            xAttrs->AddAttribute( "draw:style", "rect" );
            if ( dots1>0 ) {
                xAttrs->AddAttribute( "draw:dots1", OUString::number(dots1) );
                xAttrs->AddAttribute( "draw:dots1-length", OUString::number(pt2mm(convLength( OUString::number(dots1_length), maCurrState, 'h' )))+"mm" );
            }
            xAttrs->AddAttribute( "draw:distance", OUString::number(pt2mm(convLength( OUString::number(dash_distance), maCurrState, 'h' )))+"mm" );
            if ( dots2>0 ) {
                xAttrs->AddAttribute( "draw:dots2", OUString::number(dots2) );
                xAttrs->AddAttribute( "draw:dots2-length", OUString::number(pt2mm(convLength( OUString::number(dots2_length), maCurrState, 'h' )))+"mm" );
            }

            mxDocumentHandler->startElement( "draw:stroke-dash", xUnoAttrs);
            mxDocumentHandler->endElement( "draw:stroke-dash" );
        }
    }

    void SvgDashArray2Odf( sal_Int32 *dots1, double *dots1_length, sal_Int32 *dots2, double *dots2_length, double *dash_distance )
    {
        *dots1 = 0;
        *dots1_length = 0;
        *dots2 = 0;
        *dots2_length = 0;
        *dash_distance = 0;

        if( maCurrState.maDashArray.empty() ) {
            return;
        }

        double effective_dasharray_size = maCurrState.maDashArray.size();
        if( maCurrState.maDashArray.size() % 2 == 1 )
            effective_dasharray_size = maCurrState.maDashArray.size()*2;

        *dash_distance = maCurrState.maDashArray[1%maCurrState.maDashArray.size()];
        sal_Int32 dist_count = 1;
        for( int i=3; i<effective_dasharray_size; i+=2 ) {
            *dash_distance = ((dist_count * *dash_distance) + maCurrState.maDashArray[i%maCurrState.maDashArray.size()])/(dist_count+1);
            ++dist_count;
        }

        *dots1 = 1;
        *dots1_length = maCurrState.maDashArray[0];
        int i=2;
        while( ( i<effective_dasharray_size ) && ( maCurrState.maDashArray[i%maCurrState.maDashArray.size()] == *dots1_length ) ) {
            ++(*dots1);
            i += 2;
        }
        if( i<effective_dasharray_size ) {
            *dots2 = 1;
            *dots2_length = maCurrState.maDashArray[i];
            i+=2;
            while( ( i<effective_dasharray_size ) && ( maCurrState.maDashArray[i%maCurrState.maDashArray.size()] == *dots2_length ) ) {
                ++(*dots2);
                i += 2;
            }
        }

        SAL_INFO("filter.svg", "SvgDashArray2Odf " << *dash_distance << " " << *dots1 << " " << *dots1_length << " " << *dots2 << " " << *dots2_length );
    }

    static void push() {}
    static void pop()  {}

    State                                      maCurrState;
    StateMap&                                  mrStateMap;
    uno::Reference<xml::sax::XDocumentHandler> mxDocumentHandler;
};

static void writeOfficeStyles(  StateMap&                                         rStateMap,
                                const uno::Reference<xml::dom::XElement>&         rElem,
                                const uno::Reference<xml::sax::XDocumentHandler>& xDocHdl )
{
    OfficeStylesWritingVisitor aVisitor( rStateMap, xDocHdl );
    visitElements( aVisitor, rElem, STYLE_WRITER );

}

#ifdef DEBUG_FILTER_SVGREADER
struct DumpingVisitor
{
    void operator()( const uno::Reference<xml::dom::XElement>& xElem )
    {
        SAL_WARN("filter", "name: " << xElem->getTagName());
    }

    void operator()( const uno::Reference<xml::dom::XElement>&      xElem,
                     const uno::Reference<xml::dom::XNamedNodeMap>& xAttributes )
    {
        SAL_WARN("filter", "name: " << xElem->getTagName());
        const sal_Int32 nNumAttrs( xAttributes->getLength() );
        for( sal_Int32 i=0; i<nNumAttrs; ++i )
        {
            SAL_WARN("filter", xAttributes->item(i)->getNodeName() << "=" << xAttributes->item(i)->getNodeValue());
        }
    }

    void push() {}
    void pop()  {}
};

static void dumpTree( const uno::Reference<xml::dom::XElement> xElem )
{
    DumpingVisitor aVisitor;
    visitElements(aVisitor, xElem, STYLE_ANNOTATOR);
}
#endif


SVGReader::SVGReader(const uno::Reference<uno::XComponentContext>&     xContext,
                     const uno::Reference<io::XInputStream>&           xInputStream,
                     const uno::Reference<xml::sax::XDocumentHandler>& xDocumentHandler) :
    SVGReader(xContext, xInputStream, xDocumentHandler, dynamic_cast<SvXMLImport *>(xDocumentHandler.get()))
{
}

SVGReader::SVGReader(const uno::Reference<uno::XComponentContext>&     xContext,
                     const uno::Reference<io::XInputStream>&           xInputStream,
                     const uno::Reference<xml::sax::XDocumentHandler>& xDocumentHandler,
                     SvXMLImport *pFastHandler) :
    m_xContext( xContext ),
    m_xInputStream( xInputStream ),
    m_xDocumentHandler( pFastHandler != nullptr ? new SvXMLLegacyToFastDocHandler(pFastHandler) : xDocumentHandler )
{
}

bool SVGReader::parseAndConvert()
{
    uno::Reference<xml::dom::XDocumentBuilder> xDomBuilder = xml::dom::DocumentBuilder::create(m_xContext);

    uno::Reference<xml::dom::XDocument> xDom(
        xDomBuilder->parse(m_xInputStream),
        uno::UNO_QUERY_THROW );

    uno::Reference<xml::dom::XElement> xDocElem( xDom->getDocumentElement(),
                                                 uno::UNO_QUERY_THROW );

    // the root state for svg document
    State aInitialState;


    // doc boilerplate


    m_xDocumentHandler->startDocument();

    // get the document dimensions

    // if the "width" and "height" attributes are missing, inkscape fakes
    // A4 portrait for. Let's do the same.
    if (!xDocElem->hasAttribute("width"))
        xDocElem->setAttribute("width", "210mm");
    if (!xDocElem->hasAttribute("height"))
        xDocElem->setAttribute("height", "297mm");

    double fViewPortWidth( pt2mm(convLength(xDocElem->getAttribute("width"),aInitialState,'h')) );
    double fViewPortHeight( pt2mm(convLength(xDocElem->getAttribute("height"),aInitialState,'v')) );

    // document prolog
    rtl::Reference<SvXMLAttributeList> xAttrs( new SvXMLAttributeList() );
    uno::Reference<xml::sax::XAttributeList> xUnoAttrs( xAttrs.get() );

    xAttrs->AddAttribute( "xmlns:office", OASIS_STR "office:1.0" );
    xAttrs->AddAttribute( "xmlns:style", OASIS_STR "style:1.0" );
    xAttrs->AddAttribute( "xmlns:text", OASIS_STR "text:1.0" );
    xAttrs->AddAttribute( "xmlns:svg", OASIS_STR "svg-compatible:1.0" );
    xAttrs->AddAttribute( "xmlns:table", OASIS_STR "table:1.0" );
    xAttrs->AddAttribute( "xmlns:draw", OASIS_STR "drawing:1.0" );
    xAttrs->AddAttribute( "xmlns:fo", OASIS_STR "xsl-fo-compatible:1.0" );
    xAttrs->AddAttribute( "xmlns:xlink", "http://www.w3.org/1999/xlink");
    xAttrs->AddAttribute( "xmlns:dc", "http://purl.org/dc/elements/1.1/");
    xAttrs->AddAttribute( "xmlns:number", OASIS_STR "datastyle:1.0" );
    xAttrs->AddAttribute( "xmlns:presentation", OASIS_STR "presentation:1.0" );
    xAttrs->AddAttribute( "xmlns:math", "http://www.w3.org/1998/Math/MathML");
    xAttrs->AddAttribute( "xmlns:form", OASIS_STR "form:1.0" );
    xAttrs->AddAttribute( "xmlns:script", OASIS_STR "script:1.0" );
    xAttrs->AddAttribute( "xmlns:config", OASIS_STR "config:1.0" );
    xAttrs->AddAttribute( "xmlns:dom", "http://www.w3.org/2001/xml-events");
    xAttrs->AddAttribute( "xmlns:xforms", "http://www.w3.org/2002/xforms");
    xAttrs->AddAttribute( "xmlns:xsd", "http://www.w3.org/2001/XMLSchema");
    xAttrs->AddAttribute( "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
    xAttrs->AddAttribute( "office:version", "1.0");
    xAttrs->AddAttribute( "office:mimetype", "application/vnd.oasis.opendocument.graphics");

    m_xDocumentHandler->startElement( "office:document", xUnoAttrs );

    xAttrs->Clear();

    m_xDocumentHandler->startElement( "office:settings", xUnoAttrs);

    xAttrs->AddAttribute( "config:name", "ooo:view-settings");
    m_xDocumentHandler->startElement( "config:config-item-set", xUnoAttrs);

    xAttrs->Clear();

    xAttrs->AddAttribute( "config:name", "VisibleAreaTop");
    xAttrs->AddAttribute( "config:type", "int");
    m_xDocumentHandler->startElement( "config:config-item", xUnoAttrs);

    m_xDocumentHandler->characters( "0" );

    m_xDocumentHandler->endElement( "config:config-item" );

    xAttrs->Clear();

    xAttrs->AddAttribute( "config:name", "VisibleAreaLeft" );
    xAttrs->AddAttribute( "config:type", "int" );
    m_xDocumentHandler->startElement( "config:config-item" , xUnoAttrs);

    m_xDocumentHandler->characters( "0" );

    m_xDocumentHandler->endElement( "config:config-item" );

    xAttrs->Clear();

    xAttrs->AddAttribute( "config:name" , "VisibleAreaWidth" );
    xAttrs->AddAttribute( "config:type" , "int" );
    m_xDocumentHandler->startElement( "config:config-item" , xUnoAttrs);

    sal_Int64 iWidth = sal_Int64(fViewPortWidth);
    m_xDocumentHandler->characters( OUString::number(iWidth) );

    m_xDocumentHandler->endElement( "config:config-item" );

    xAttrs->Clear();

    xAttrs->AddAttribute( "config:name", "VisibleAreaHeight" );
    xAttrs->AddAttribute( "config:type", "int" );
    m_xDocumentHandler->startElement( "config:config-item", xUnoAttrs);

    sal_Int64 iHeight = sal_Int64(fViewPortHeight);
    m_xDocumentHandler->characters( OUString::number(iHeight) );

    m_xDocumentHandler->endElement( "config:config-item" );

    m_xDocumentHandler->endElement( "config:config-item-set" );

    m_xDocumentHandler->endElement( "office:settings" );

    xAttrs->Clear();

    m_xDocumentHandler->startElement( "office:automatic-styles",
                                      xUnoAttrs );

    xAttrs->AddAttribute( "style:name", "pagelayout1");
    m_xDocumentHandler->startElement( "style:page-layout", xUnoAttrs );
    // TODO(Q3): this is super-ugly. In-place container come to mind.
    xAttrs->Clear();

    // make page viewport-width times viewport-height mm large - add
    // 5% border at every side
    xAttrs->AddAttribute( "fo:margin-top", "0mm");
    xAttrs->AddAttribute( "fo:margin-bottom", "0mm");
    xAttrs->AddAttribute( "fo:margin-left", "0mm");
    xAttrs->AddAttribute( "fo:margin-right", "0mm");
    xAttrs->AddAttribute( "fo:page-width", OUString::number(fViewPortWidth)+"mm");
    xAttrs->AddAttribute( "fo:page-height", OUString::number(fViewPortHeight)+"mm");
    xAttrs->AddAttribute( "style:print-orientation",
        fViewPortWidth > fViewPortHeight ? OUString("landscape") : OUString("portrait") );
    m_xDocumentHandler->startElement( "style:page-layout-properties", xUnoAttrs );
    m_xDocumentHandler->endElement( "style:page-layout-properties" );
    m_xDocumentHandler->endElement( "style:page-layout" );

    xAttrs->Clear();
    xAttrs->AddAttribute( "style:name", "pagestyle1" );
    xAttrs->AddAttribute( "style:family", "drawing-page" );
    m_xDocumentHandler->startElement( "style:style", xUnoAttrs );

    xAttrs->Clear();
    xAttrs->AddAttribute( "draw:background-size", "border");
    xAttrs->AddAttribute( "draw:fill", "none");
    m_xDocumentHandler->startElement( "style:drawing-page-properties", xUnoAttrs );
    m_xDocumentHandler->endElement( "style:drawing-page-properties" );
    m_xDocumentHandler->endElement( "style:style" );

    StatePool aStatePool;
    StateMap  aStateMap;
    std::vector< uno::Reference<xml::dom::XElement> > aUseElementVector;

    annotateStyles(aStatePool,aStateMap,aInitialState,
                   xDocElem,m_xDocumentHandler,aUseElementVector);

#ifdef DEBUG_FILTER_SVGREADER
    dumpTree(xDocElem);
#endif

    m_xDocumentHandler->endElement( "office:automatic-styles" );

    xAttrs->Clear();
    m_xDocumentHandler->startElement( "office:styles", xUnoAttrs);
    writeOfficeStyles( aStateMap,
                       xDocElem,
                       m_xDocumentHandler);
    m_xDocumentHandler->endElement( "office:styles" );


    m_xDocumentHandler->startElement( "office:master-styles", xUnoAttrs );
    xAttrs->Clear();
    xAttrs->AddAttribute( "style:name", "Default");
    xAttrs->AddAttribute( "style:page-layout-name", "pagelayout1");
    xAttrs->AddAttribute( "draw:style-name", "pagestyle1");
    m_xDocumentHandler->startElement( "style:master-page", xUnoAttrs );
    m_xDocumentHandler->endElement( "style:master-page" );

    m_xDocumentHandler->endElement( "office:master-styles" );


    xAttrs->Clear();
    m_xDocumentHandler->startElement( "office:body", xUnoAttrs );
    m_xDocumentHandler->startElement( "office:drawing", xUnoAttrs );

    xAttrs->Clear();
    xAttrs->AddAttribute( "draw:master-page-name", "Default");
    xAttrs->AddAttribute( "draw:style-name", "pagestyle1");
    m_xDocumentHandler->startElement("draw:page", xUnoAttrs);

    // write out all shapes
    writeShapes(aStateMap,
                xDocElem,
                m_xDocumentHandler,
                aUseElementVector);

    m_xDocumentHandler->endElement( "draw:page" );
    m_xDocumentHandler->endElement( "office:drawing" );
    m_xDocumentHandler->endElement( "office:body" );
    m_xDocumentHandler->endElement( "office:document" );
    m_xDocumentHandler->endDocument();

    return true;
}

} // namespace svgi

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/filter/source/svg/test/parsertest.cxx b/filter/source/svg/test/parsertest.cxx
deleted file mode 100644
index 8cb367e..0000000
--- a/filter/source/svg/test/parsertest.cxx
+++ /dev/null
@@ -1,172 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include <sal/types.h>
#include <cppunit/TestAssert.h>
#include <cppunit/TestFixture.h>
#include <cppunit/extensions/HelperMacros.h>

#include "gfxtypes.hxx"
#include "parserfragments.hxx"

using namespace svgi;

class TestParser : public CppUnit::TestFixture
{
public:
    void testParseColor()
    {
        ARGBColor aTmp;

        const char* sIn="#102030  ";
        ARGBColor aOut(16, 32, 48);
        CPPUNIT_ASSERT_MESSAGE( "Consuming color #112233",
                                parseColor( sIn, aTmp ) );
        CPPUNIT_ASSERT_MESSAGE( "Parsing color #112233",
                                aOut==aTmp );

        sIn="  #321";
        aOut=ARGBColor(51, 34, 17);
        CPPUNIT_ASSERT_MESSAGE( "Consuming color #321",
                                parseColor( sIn, aTmp ) );
        CPPUNIT_ASSERT_MESSAGE( "Parsing color #321",
                                aOut==aTmp );

        sIn="rgb(100,200,\t 50)";
        aOut=ARGBColor(100, 200, 50);
        CPPUNIT_ASSERT_MESSAGE( "Consuming color rgb(100,200,50)",
                                parseColor( sIn, aTmp ) );
        CPPUNIT_ASSERT_MESSAGE( "Parsing color rgb(100,200,50)",
                                aOut==aTmp );

        sIn="rgb(0.1, \t0.2,0.9)";
        aOut=ARGBColor(0.1, 0.2, 0.9);
        CPPUNIT_ASSERT_MESSAGE( "Consuming color rgb(0.1,0.2,0.9)",
                                parseColor( sIn, aTmp ) );
        CPPUNIT_ASSERT_MESSAGE( "Parsing color rgb(0.1,0.2,0.9)",
                                aOut==aTmp );

        sIn=" burlywood ";
        aOut=ARGBColor(222,184,135);
        CPPUNIT_ASSERT_MESSAGE( "Consuming color burlywood",
                                parseColor( sIn, aTmp ) );
        CPPUNIT_ASSERT_MESSAGE( "Parsing color burlywood",
                                aOut==aTmp );
    }

    void testParseOpacity()
    {
        ARGBColor aTmp;

        const char* sIn=" 0.123  ";
        ARGBColor aOut(0.123, 0.0, 0.0, 0.0);
        CPPUNIT_ASSERT_MESSAGE( "Consuming opacity 0.123",
                                parseOpacity( sIn, aTmp ) );
        CPPUNIT_ASSERT_MESSAGE( "Parsing opacity 0.123",
                                aOut==aTmp );
    }

    void testParseTransform()
    {
        basegfx::B2DHomMatrix aOut;

        const char* sIn=" none  ";
        basegfx::B2DHomMatrix aTmp;
        CPPUNIT_ASSERT_MESSAGE( "Consuming transformation none",
                                parseTransform( sIn, aTmp ) );
        CPPUNIT_ASSERT_MESSAGE( "Parsing transformation none",
                                aOut==aTmp );

        sIn=" scale( 10 )  ";
        aOut.identity();
        aOut.scale(10.0,10.0);
        CPPUNIT_ASSERT_MESSAGE( "Consuming transformation scale(10)",
                                parseTransform( sIn, aTmp ) );
        CPPUNIT_ASSERT_MESSAGE( "Parsing transformation scale(10)",
                                aOut==aTmp );

        sIn=" scale( 10 20.12 )  ";
        aOut.identity();
        aOut.scale(10.0,20.12);
        CPPUNIT_ASSERT_MESSAGE( "Consuming transformation scale(10 20.12)",
                                parseTransform( sIn, aTmp ) );
        CPPUNIT_ASSERT_MESSAGE( "Parsing transformation scale(10 20.12)",
                                aOut==aTmp );

        sIn="matrix( 1,2 3,4,5 6 )";
        aOut.identity();
        aOut.set(0,0,1.0); aOut.set(1,0,2.0); aOut.set(0,1,3.0); aOut.set(1,1,4.0); aOut.set(0,2,5.0); aOut.set(1,2,6.0);
        CPPUNIT_ASSERT_MESSAGE( "Consuming transformation matrix(1,2,3,4,5,6)",
                                parseTransform( sIn, aTmp ) );
        CPPUNIT_ASSERT_MESSAGE( "Parsing transformation matrix(1,2,3,4,5,6)",
                                aOut==aTmp );

        sIn="matrix( 1 0 0 1 -10 -10 ) translate(10) scale(10), rotate(90)";
        aOut.identity();
        aOut.set(0,0,0.0); aOut.set(1,0,10.0); aOut.set(0,1,-10.0); aOut.set(1,1,0.0); aOut.set(0,2,0.0); aOut.set(1,2,0.0);
        CPPUNIT_ASSERT_MESSAGE( "Consuming transformation matrix(1,2,3,4,5,6)",
                                parseTransform( sIn, aTmp ) );
        CPPUNIT_ASSERT_MESSAGE( "Parsing transformation matrix(1,2,3,4,5,6)",
                                aOut==aTmp );

        sIn="skewX(45)";
        aOut.identity();
        aOut.set(0,0,1.0); aOut.set(1,0,1.0); aOut.set(0,1,0.0); aOut.set(1,1,1.0); aOut.set(0,2,0.0); aOut.set(1,2,0.0);
        CPPUNIT_ASSERT_MESSAGE( "Consuming transformation skewX(45)",
                                parseTransform( sIn, aTmp ) );
        CPPUNIT_ASSERT_MESSAGE( "Parsing transformation skewX(45)",
                                aOut==aTmp );

        sIn="skewY(45)";
        aOut.identity();
        aOut.set(0,0,1.0); aOut.set(1,0,0.0); aOut.set(0,1,1.0); aOut.set(1,1,1.0); aOut.set(0,2,0.0); aOut.set(1,2,0.0);
        CPPUNIT_ASSERT_MESSAGE( "Consuming transformation skewY(45)",
                                parseTransform( sIn, aTmp ) );
        CPPUNIT_ASSERT_MESSAGE( "Parsing transformation skewY(45)",
                                aOut==aTmp );
    }

    void testParseViewBox()
    {
        basegfx::B2DRange aTmp;

        const char* sIn=" 10 20, 30.5,5  ";
        basegfx::B2DRange aOut(10,20,40.5,25);
        CPPUNIT_ASSERT_MESSAGE( "Consuming 10,20,30.5,5",
                                parseViewBox( sIn, aTmp ) );
        CPPUNIT_ASSERT_MESSAGE( "Parsing 10,20,30.5,5",
                                aOut==aTmp );
    }

    void testParseDashArray()
    {
        std::vector<double> aTmp;

        const char* sIn=" 10,20, -10.00  ";
        std::vector<double> aOut; aOut.push_back(10.0); aOut.push_back(20.0); aOut.push_back(-10.0);
        CPPUNIT_ASSERT_MESSAGE( "Consuming 10,20,-10.00",
                                parseDashArray( sIn, aTmp ) );
        CPPUNIT_ASSERT_MESSAGE( "Parsing 10,20,-10.00",
                                aOut==aTmp );
    }

    CPPUNIT_TEST_SUITE(TestParser);
    CPPUNIT_TEST(testParseColor);
    CPPUNIT_TEST(testParseOpacity);
    CPPUNIT_TEST(testParseTransform);
    CPPUNIT_TEST(testParseViewBox);
    CPPUNIT_TEST(testParseDashArray);
    // TODO: CPPUNIT_TEST(testParseXlinkHref);
    CPPUNIT_TEST_SUITE_END();
};


CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(TestParser, "test svg parser fragments");

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/filter/source/svg/test/svg2odf.cxx b/filter/source/svg/test/svg2odf.cxx
deleted file mode 100644
index 524c2e7..0000000
--- a/filter/source/svg/test/svg2odf.cxx
+++ /dev/null
@@ -1,134 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */

#include <svgreader.hxx>
#include "odfserializer.hxx"

#include <sal/main.h>
#include <osl/file.hxx>
#include <osl/process.h>
#include <rtl/bootstrap.hxx>

#include <cppuhelper/implbase.hxx>
#include <cppuhelper/bootstrap.hxx>
#include <comphelper/processfactory.hxx>
#include <comphelper/oslfile2streamwrap.hxx>

using namespace ::com::sun::star;

namespace
{
    class OutputWrap : public cppu::WeakImplHelper<
        io::XOutputStream>
    {
        osl::File maFile;

    public:

        explicit OutputWrap( const OUString& rURL ) : maFile(rURL)
        {
            maFile.open( osl_File_OpenFlag_Create|osl_File_OpenFlag_Write );
        }

        virtual void SAL_CALL writeBytes( const css::uno::Sequence< ::sal_Int8 >& aData ) override

        {
            sal_uInt64 nBytesWritten(0);
            maFile.write(aData.getConstArray(),aData.getLength(),nBytesWritten);
        }

        virtual void SAL_CALL flush() override
        {
        }

        virtual void SAL_CALL closeOutput() override
        {
            maFile.close();
        }
    };
}

SAL_IMPLEMENT_MAIN_WITH_ARGS(argc, argv)
{
    if( argc != 4 )
    {
        SAL_WARN("filter.svg", "Invocation: svg2odf <base_url> <dst_url> <ini_file>. Exiting" );
        return 1;
    }

    int nRet = 1;

    try
    {
        OUString aBaseURL, aTmpURL, aSrcURL, aDstURL, aIniUrl;

        osl_getProcessWorkingDir(&aBaseURL.pData);
        osl_getFileURLFromSystemPath( OUString::createFromAscii(argv[1]).pData,
                                      &aTmpURL.pData );
        osl_getAbsoluteFileURL(aBaseURL.pData,aTmpURL.pData,&aSrcURL.pData);

        osl_getFileURLFromSystemPath( OUString::createFromAscii(argv[2]).pData,
                                      &aTmpURL.pData );
        osl_getAbsoluteFileURL(aBaseURL.pData,aTmpURL.pData,&aDstURL.pData);

        osl_getFileURLFromSystemPath( OUString::createFromAscii(argv[3]).pData,
                                    &aTmpURL.pData );
        osl_getAbsoluteFileURL(aBaseURL.pData,aTmpURL.pData,&aIniUrl.pData);

        // bootstrap UNO
        uno::Reference< lang::XMultiServiceFactory > xFactory;
        uno::Reference< uno::XComponentContext > xCtx;
        xCtx = ::cppu::defaultBootstrap_InitialComponentContext(aIniUrl);
        xFactory.set(xCtx->getServiceManager(), uno::UNO_QUERY);
        if (!xFactory.is())
        {
            SAL_WARN("filter.svg", "Could not bootstrap UNO, installation must be in disorder. Exiting." );
            return 1;
        }

        ::comphelper::setProcessServiceFactory( xFactory );

        osl::File aInputFile(aSrcURL);
        if( osl::FileBase::E_None!=aInputFile.open(osl_File_OpenFlag_Read) )
        {
            SAL_WARN("filter.svg", "Cannot open input file" );
            return 1;
        }

        svgi::SVGReader aReader(xCtx,
                                uno::Reference<io::XInputStream>(
                                    new comphelper::OSLInputStreamWrapper(aInputFile)),
                                svgi::createSerializer(new OutputWrap(aDstURL)));
        nRet = aReader.parseAndConvert() ? 0 : 1;

    }
    catch (const uno::Exception& e)
    {
        SAL_WARN("filter.svg", "Fatal exception: " << e);
        return 1;
    }
    catch (const std::exception &e)
    {
        SAL_WARN("filter.svg", "Fatal exception: " << e.what());
        return 1;
    }
    return nRet;
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/filter/source/svg/tokenmap.cxx b/filter/source/svg/tokenmap.cxx
deleted file mode 100644
index 3362803..0000000
--- a/filter/source/svg/tokenmap.cxx
+++ /dev/null
@@ -1,74 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */

#include "tokenmap.hxx"
#include <string.h>

namespace svgi
{

#if defined __clang__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
#if __has_warning("-Wdeprecated-register")
#pragma GCC diagnostic ignored "-Wdeprecated-register"
#endif
#endif
#include <tokens.cxx>
#if defined __clang__
#pragma GCC diagnostic pop
#endif

sal_Int32 getTokenId( const char* sIdent, sal_Int32 nLen )
{
    const struct xmltoken* t = Perfect_Hash::in_word_set( sIdent, nLen );
    if( t )
        return t->nToken;
    else
        return XML_TOKEN_INVALID;
}

sal_Int32 getTokenId( const OUString& sIdent )
{
    OString aUTF8( sIdent.getStr(),
                        sIdent.getLength(),
                        RTL_TEXTENCODING_UTF8 );
    return getTokenId( aUTF8.getStr(), aUTF8.getLength() );
}

const char* getTokenName( sal_Int32 nTokenId )
{
    if( nTokenId >= XML_TOKEN_COUNT )
        return nullptr;

    const xmltoken* pCurr=wordlist;
    const xmltoken* pEnd=wordlist+SAL_N_ELEMENTS(wordlist);
    while( pCurr != pEnd )
    {
        if(pCurr->nToken == nTokenId)
            return pCurr->name;
        ++pCurr;
    }

    return nullptr;
}

} // namespace svgi

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/filter/source/svg/tokenmap.hxx b/filter/source/svg/tokenmap.hxx
deleted file mode 100644
index b227d07..0000000
--- a/filter/source/svg/tokenmap.hxx
+++ /dev/null
@@ -1,27 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */
#ifndef INCLUDED_FILTER_SOURCE_SVG_TOKENMAP_HXX
#define INCLUDED_FILTER_SOURCE_SVG_TOKENMAP_HXX

#include <tokens.hxx>

#include <rtl/string.hxx>
#include <rtl/ustring.hxx>

namespace svgi
{
    sal_Int32   getTokenId( const char* sIdent, sal_Int32 nLen );
    sal_Int32   getTokenId( const OUString& sIdent );
    const char* getTokenName( sal_Int32 nTokenId );

} // namespace svgi

#endif

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/filter/source/svg/units.cxx b/filter/source/svg/units.cxx
deleted file mode 100644
index d3537b3..0000000
--- a/filter/source/svg/units.cxx
+++ /dev/null
@@ -1,143 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include "units.hxx"
#include <basegfx/range/b2drange.hxx>
#include <gfxtypes.hxx>
#include <rtl/ustring.hxx>
#include <boost/spirit/include/classic.hpp>

namespace svgi
{

double convLength( const OUString& value, SvgUnit unit, const State& rState, char dir )
{
    // convert svg unit to internal coordinates ("pixel"). Since the
    // OOo drawing layer is still largely integer-based, the initial
    // viewport transformation includes a certain scale factor
    double fRet(value.toDouble());
    switch ( unit )
    {
        case SVG_LENGTH_UNIT_CM: fRet *= F_SVG_PIXEL_PER_INCH/2.54; break;
        case SVG_LENGTH_UNIT_IN: fRet *= F_SVG_PIXEL_PER_INCH; break;
        case SVG_LENGTH_UNIT_MM: fRet *= F_SVG_PIXEL_PER_INCH/25.4; break;
        case SVG_LENGTH_UNIT_PC: fRet *= F_SVG_PIXEL_PER_INCH/6.0; break;
        case SVG_LENGTH_UNIT_PT: fRet *= F_SVG_PIXEL_PER_INCH/72.0; break;
        case SVG_LENGTH_UNIT_EM: fRet *= rState.mnFontSize; break;
        case SVG_LENGTH_UNIT_EX: fRet *= rState.mnFontSize / 2.0; break;
        case SVG_LENGTH_UNIT_USER:
        case SVG_LENGTH_UNIT_PX:
            // no unit defaults to PX in svg, assume display to have 90DPI
            break;
        case SVG_LENGTH_FONT_SIZE:
        {
            //In CSS2, the suggested scaling factor between adjacent indexes is 1.2
            if ( value == "xx-small" )
                fRet = rState.mnFontSize / 1.728;
            else if ( value == "x-small" )
                fRet = rState.mnFontSize / 1.44;
            else if ( value == "small" )
                fRet = rState.mnFontSize / 1.2;
            else if ( value == "smaller" )
                fRet = rState.mnParentFontSize / 1.2;
            else if ( value == "initial" || value == "medium" )
                fRet = rState.mnFontSize;
            else if ( value == "larger" )
                fRet = rState.mnParentFontSize * 1.2;
            else if ( value == "large" )
                fRet = rState.mnFontSize * 1.2;
            else if ( value == "x-large" )
                fRet = rState.mnFontSize * 1.44;
            else if ( value == "xx-large" )
                fRet = rState.mnFontSize * 1.728;

            break;
        }
        case SVG_LENGTH_UNIT_PERCENTAGE:
        {
            double fBoxLen;
            if (rState.maViewBox.isEmpty())
            {
                basegfx::B2DRange aDefaultBox(0, 0,
                  convLength("210", SVG_LENGTH_UNIT_MM, rState, 'h'),
                  convLength("297", SVG_LENGTH_UNIT_MM, rState, 'v'));
                fBoxLen = (dir=='h' ? aDefaultBox.getWidth() :
                          (dir=='v' ? aDefaultBox.getHeight() :
                           aDefaultBox.getRange().getLength()));
            }
            else
            {
                fBoxLen = (dir=='h' ? rState.maViewBox.getWidth() :
                          (dir=='v' ? rState.maViewBox.getHeight() :
                           rState.maViewBox.getRange().getLength()));
            }

            fRet *= fBoxLen/100.0;
        }
        break;
        default: SAL_WARN("filter.svg", "Unknown length type" ); break;
    }

    return fRet;
}

double convLength( const OUString& sValue, const State& rState, char dir )
{
    //FIXME: convert deprecated spirit::classic to use spirit::qi
    using namespace ::boost::spirit::classic;

    OString aUTF8 = OUStringToOString( sValue,
                                                 RTL_TEXTENCODING_UTF8 );

    std::string sVal;
    SvgUnit eUnit=SVG_LENGTH_UNIT_PX;
    const bool bRes = parse(aUTF8.getStr(),
        //  Begin grammar
        (
            //parse font-size keywords (ie: xx-large, medium )
            ( +alpha_p >> !(str_p("-") >> +alpha_p) )[assign_a(sVal)]
                >> str_p("")[assign_a(eUnit,SVG_LENGTH_FONT_SIZE)] |
            //take the first part and ignore the units
            ( +(anychar_p -
             (str_p("cm") |
             str_p("em") |
             str_p("ex") |
             str_p("in") |
             str_p("mm") |
             str_p("pc") |
             str_p("pt") |
             str_p("px") |
             str_p("%")))
            )[assign_a(sVal)]
            >> (  str_p("cm") [assign_a(eUnit,SVG_LENGTH_UNIT_CM)]
                | str_p("em") [assign_a(eUnit,SVG_LENGTH_UNIT_EM)]
                | str_p("ex") [assign_a(eUnit,SVG_LENGTH_UNIT_EX)]
                | str_p("in") [assign_a(eUnit,SVG_LENGTH_UNIT_IN)]
                | str_p("mm") [assign_a(eUnit,SVG_LENGTH_UNIT_MM)]
                | str_p("pc") [assign_a(eUnit,SVG_LENGTH_UNIT_PC)]
                | str_p("pt") [assign_a(eUnit,SVG_LENGTH_UNIT_PT)]
                | str_p("px") [assign_a(eUnit,SVG_LENGTH_UNIT_PX)]
                | str_p("%") [assign_a(eUnit,SVG_LENGTH_UNIT_PERCENTAGE)]
                | str_p("") [assign_a(eUnit,SVG_LENGTH_UNIT_USER)]
                | end_p)
        ),
        //  End grammar
        space_p).full;

    if( !bRes )
        return 0.0;

    OUString oVal = OUString::createFromAscii(sVal.c_str()).replaceAll(",",".");

    return convLength(oVal,eUnit,rState,dir);
}

} // namespace svgi

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/filter/source/svg/units.hxx b/filter/source/svg/units.hxx
deleted file mode 100644
index 06b7217..0000000
--- a/filter/source/svg/units.hxx
+++ /dev/null
@@ -1,59 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */
#ifndef INCLUDED_FILTER_SOURCE_SVG_UNITS_HXX
#define INCLUDED_FILTER_SOURCE_SVG_UNITS_HXX

#include <sal/config.h>
#include <rtl/ustring.hxx>

namespace svgi
{
    // recommended value for this device dependent unit, see CSS2 section 4.3.2 Lengths
    // Same as in svgio
    #define F_SVG_PIXEL_PER_INCH  90.0

    struct State;
    enum SvgUnit
    {
        SVG_LENGTH_UNIT_CM,
        SVG_LENGTH_UNIT_EM,
        SVG_LENGTH_UNIT_EX,
        SVG_LENGTH_UNIT_IN,
        SVG_LENGTH_UNIT_MM,
        SVG_LENGTH_UNIT_PC,
        SVG_LENGTH_UNIT_PT,
        SVG_LENGTH_UNIT_PX,
        SVG_LENGTH_UNIT_PERCENTAGE,
        SVG_LENGTH_UNIT_USER,
        SVG_LENGTH_FONT_SIZE
    };

    /** return svg_length_t in 100th's of mm
         @param fVal value to convert
         @param unit unit the value is in
         @param rState current state (needed for viewport dimensions etc.)
         @param dir direction - either 'h' or 'v' for horizonal or vertical, resp.
     */
    double convLength( const OUString& sVal, SvgUnit unit, const State& rState, char dir );

    /** return svg_length_t in 100th's of mm
         @param sValue value to convert
         @param rState current state (needed for viewport dimensions etc.)
         @param dir direction - either 'h' or 'v' for horizonal or vertical, resp.
     */
    double convLength( const OUString& sValue, const State& rState, char dir );

    inline double pt2mm(double fVal) { return fVal*25.4/72.0; }
    inline double pt100thmm(double fVal) { return fVal*2540.0/72.0; }

} // namespace svgi

#endif

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/svx/svdmodel.hxx b/include/svx/svdmodel.hxx
index 0b7b5ab..cc5ad89b 100644
--- a/include/svx/svdmodel.hxx
+++ b/include/svx/svdmodel.hxx
@@ -214,8 +214,16 @@ public:
    sal_uInt16 getHandoutPageCount() const { return mnHandoutPageCount; }
    void setHandoutPageCount( sal_uInt16 nHandoutPageCount ) { mnHandoutPageCount = nHandoutPageCount; }

protected:
    // Adapt to given Size and Borders scaling all contained data, maybe
    // including PresObj's in higher derivations
    virtual void adaptSizeAndBorderForAllPages(
        const Size& rNewSize,
        long nLeft = 0,
        long nRight = 0,
        long nUpper = 0,
        long nLower = 0);

protected:
    virtual css::uno::Reference< css::uno::XInterface > createUnoModel();

private:
diff --git a/sd/inc/drawdoc.hxx b/sd/inc/drawdoc.hxx
index ac824cf..a302fa6e 100644
--- a/sd/inc/drawdoc.hxx
+++ b/sd/inc/drawdoc.hxx
@@ -77,6 +77,7 @@ struct SpellCallbackInfo;
class SdDrawDocument;
class SdCustomShow;
class SdCustomShowList;
class SdUndoGroup;

namespace sd
{
@@ -202,8 +203,34 @@ protected:
public:


                        SAL_DLLPRIVATE SdDrawDocument(DocumentType eType, SfxObjectShell* pDocSh);
                        SAL_DLLPRIVATE virtual ~SdDrawDocument() override;
    SAL_DLLPRIVATE SdDrawDocument(DocumentType eType, SfxObjectShell* pDocSh);
    SAL_DLLPRIVATE virtual ~SdDrawDocument() override;

    // Adapt to given Size and Borders scaling all contained data, maybe
    // including PresObj's in higher derivations
    virtual void adaptSizeAndBorderForAllPages(
        const Size& rNewSize,
        long nLeft = 0,
        long nRight = 0,
        long nUpper = 0,
        long nLower = 0) override;

    // Adapt PageSize for all Pages of PageKind ePageKind. Also
    // set Borders to left/right/upper/lower, ScaleAll, Orientation,
    // PaperBin and BackgroundFullSize. Create Undo-Actions when
    // a SdUndoGroup is given (then used from the View probably)
    void AdaptPageSizeForAllPages(
        const Size& rNewSize,
        PageKind ePageKind,
        SdUndoGroup* pUndoGroup = nullptr,
        long nLeft = 0,
        long nRight = 0,
        long nUpper = 0,
        long nLower = 0,
        bool bScaleAll = false,
        Orientation eOrientation = Orientation::Landscape,
        sal_uInt16 nPaperBin = 0,
        bool bBackgroundFullSize = false);

    SAL_DLLPRIVATE SdDrawDocument*     AllocSdDrawDocument() const;
    SAL_DLLPRIVATE virtual SdrModel*   AllocModel() const override; //forwards to AllocSdDrawDocument
diff --git a/sd/source/core/drawdoc.cxx b/sd/source/core/drawdoc.cxx
index 36d6a82..0c152e0 100644
--- a/sd/source/core/drawdoc.cxx
+++ b/sd/source/core/drawdoc.cxx
@@ -96,7 +96,8 @@
#include <optsitem.hxx>
#include <FrameView.hxx>
#include <undo/undomanager.hxx>

#include <sdundogr.hxx>
#include <undopage.hxx>
#include <tools/tenccvt.hxx>
#include <vcl/settings.hxx>

@@ -392,6 +393,179 @@ SdDrawDocument::~SdDrawDocument()
    mpCharClass.reset();
}

void SdDrawDocument::adaptSizeAndBorderForAllPages(
    const Size& rNewSize,
    long nLeft,
    long nRight,
    long nUpper,
    long nLower)
{
    const sal_uInt16 nMasterPageCnt(GetMasterSdPageCount(PageKind::Standard));
    const sal_uInt16 nPageCnt(GetSdPageCount(PageKind::Standard));

    if(0 == nMasterPageCnt && 0 == nPageCnt)
    {
        return;
    }

    SdPage* pPage(0 != nPageCnt ? GetSdPage(0, PageKind::Standard) : GetMasterSdPage(0, PageKind::Standard));

    // call fully implemented local version, including getting
    // some more information from one of the Pages (1st one)
    AdaptPageSizeForAllPages(
        rNewSize,
        PageKind::Standard,
        nullptr,
        nLeft,
        nRight,
        nUpper,
        nLower,
        true,
        pPage->GetOrientation(),
        pPage->GetPaperBin(),
        pPage->IsBackgroundFullSize());

    // adjust handout page to new format of the standard page
    if(0 != nPageCnt)
    {
        GetSdPage(0, PageKind::Handout)->CreateTitleAndLayout(true);
    }
}

void SdDrawDocument::AdaptPageSizeForAllPages(
    const Size& rNewSize,
    PageKind ePageKind,
    SdUndoGroup* pUndoGroup,
    long nLeft,
    long nRight,
    long nUpper,
    long nLower,
    bool bScaleAll,
    Orientation eOrientation,
    sal_uInt16 nPaperBin,
    bool bBackgroundFullSize)
{
    sal_uInt16 i;
    const sal_uInt16 nMasterPageCnt(GetMasterSdPageCount(ePageKind));
    const sal_uInt16 nPageCnt(GetSdPageCount(ePageKind));

    if(0 == nMasterPageCnt && 0 == nPageCnt)
    {
        return;
    }

    for (i = 0; i < nMasterPageCnt; i++)
    {
        // first, handle all master pages
        SdPage* pPage(GetMasterSdPage(i, ePageKind));

        if(pUndoGroup)
        {
            SdUndoAction* pUndo(
                new SdPageFormatUndoAction(
                    this,
                    pPage,
                    pPage->GetSize(),
                    pPage->GetLeftBorder(), pPage->GetRightBorder(),
                    pPage->GetUpperBorder(), pPage->GetLowerBorder(),
                    pPage->GetOrientation(),
                    pPage->GetPaperBin(),
                    pPage->IsBackgroundFullSize(),
                    rNewSize,
                    nLeft, nRight,
                    nUpper, nLower,
                    bScaleAll,
                    eOrientation,
                    nPaperBin,
                    bBackgroundFullSize));
            pUndoGroup->AddAction(pUndo);
        }

        if (rNewSize.Width() > 0 || nLeft  >= 0 || nRight >= 0 || nUpper >= 0 || nLower >= 0)
        {
            ::tools::Rectangle aNewBorderRect(nLeft, nUpper, nRight, nLower);
            pPage->ScaleObjects(rNewSize, aNewBorderRect, bScaleAll);

            if (rNewSize.Width() > 0)
            {
                pPage->SetSize(rNewSize);
            }
        }

        if( nLeft  >= 0 || nRight >= 0 || nUpper >= 0 || nLower >= 0 )
        {
            pPage->SetBorder(nLeft, nUpper, nRight, nLower);
        }

        pPage->SetOrientation(eOrientation);
        pPage->SetPaperBin( nPaperBin );
        pPage->SetBackgroundFullSize( bBackgroundFullSize );

        if ( ePageKind == PageKind::Standard )
        {
            GetMasterSdPage(i, PageKind::Notes)->CreateTitleAndLayout();
        }

        pPage->CreateTitleAndLayout();
    }

    for (i = 0; i < nPageCnt; i++)
    {
        // then, handle all pages
        SdPage* pPage(GetSdPage(i, ePageKind));

        if(pUndoGroup)
        {
            SdUndoAction* pUndo(
                new SdPageFormatUndoAction(
                    this,
                    pPage,
                    pPage->GetSize(),
                    pPage->GetLeftBorder(), pPage->GetRightBorder(),
                    pPage->GetUpperBorder(), pPage->GetLowerBorder(),
                    pPage->GetOrientation(),
                    pPage->GetPaperBin(),
                    pPage->IsBackgroundFullSize(),
                    rNewSize,
                    nLeft, nRight,
                    nUpper, nLower,
                    bScaleAll,
                    eOrientation,
                    nPaperBin,
                    bBackgroundFullSize));
            pUndoGroup->AddAction(pUndo);
        }

        if (rNewSize.Width() > 0 || nLeft  >= 0 || nRight >= 0 || nUpper >= 0 || nLower >= 0)
        {
            ::tools::Rectangle aNewBorderRect(nLeft, nUpper, nRight, nLower);
            pPage->ScaleObjects(rNewSize, aNewBorderRect, bScaleAll);

            if (rNewSize.Width() > 0)
            {
                pPage->SetSize(rNewSize);
            }
        }

        if( nLeft  >= 0 || nRight >= 0 || nUpper >= 0 || nLower >= 0 )
        {
            pPage->SetBorder(nLeft, nUpper, nRight, nLower);
        }

        pPage->SetOrientation(eOrientation);
        pPage->SetPaperBin( nPaperBin );
        pPage->SetBackgroundFullSize( bBackgroundFullSize );

        if ( ePageKind == PageKind::Standard )
        {
            SdPage* pNotesPage = GetSdPage(i, PageKind::Notes);
            pNotesPage->SetAutoLayout( pNotesPage->GetAutoLayout() );
        }

        pPage->SetAutoLayout( pPage->GetAutoLayout() );
    }
}

SdrModel* SdDrawDocument::AllocModel() const
{
    return AllocSdDrawDocument();
diff --git a/sd/source/ui/view/viewshe2.cxx b/sd/source/ui/view/viewshe2.cxx
index f204690..6c1ad846 100644
--- a/sd/source/ui/view/viewshe2.cxx
+++ b/sd/source/ui/view/viewshe2.cxx
@@ -470,139 +470,65 @@ void ViewShell::SetPageSizeAndBorder(PageKind ePageKind, const Size& rNewSize,
                                       Orientation eOrientation, sal_uInt16 nPaperBin,
                                       bool bBackgroundFullSize)
{
    SdPage* pPage = nullptr;
    SdUndoGroup* pUndoGroup = nullptr;
    pUndoGroup = new SdUndoGroup(GetDoc());
    OUString aString(SdResId(STR_UNDO_CHANGE_PAGEFORMAT));
    pUndoGroup->SetComment(aString);
    SfxViewShell* pViewShell = GetViewShell();
    OSL_ASSERT (pViewShell!=nullptr);
    const sal_uInt16 nMasterPageCnt(GetDoc()->GetMasterSdPageCount(ePageKind));
    const sal_uInt16 nPageCnt(GetDoc()->GetSdPageCount(ePageKind));

    sal_uInt16 i, nPageCnt = GetDoc()->GetMasterSdPageCount(ePageKind);
    if(0 == nPageCnt && 0 == nMasterPageCnt)
    {
        return;
    }

    SdUndoGroup* pUndoGroup(new SdUndoGroup(GetDoc()));
    pUndoGroup->SetComment(SdResId(STR_UNDO_CHANGE_PAGEFORMAT));
    Broadcast (ViewShellHint(ViewShellHint::HINT_PAGE_RESIZE_START));

    for (i = 0; i < nPageCnt; i++)
    {
        // first, handle all master pages
        pPage = GetDoc()->GetMasterSdPage(i, ePageKind);

        SdUndoAction* pUndo = new SdPageFormatUndoAction(GetDoc(), pPage,
                            pPage->GetSize(),
                            pPage->GetLeftBorder(), pPage->GetRightBorder(),
                            pPage->GetUpperBorder(), pPage->GetLowerBorder(),
                            pPage->GetOrientation(),
                            pPage->GetPaperBin(),
                            pPage->IsBackgroundFullSize(),
                            rNewSize,
                            nLeft, nRight,
                            nUpper, nLower,
                            bScaleAll,
                            eOrientation,
                            nPaperBin,
                            bBackgroundFullSize);
        pUndoGroup->AddAction(pUndo);

        if (rNewSize.Width() > 0 ||
            nLeft  >= 0 || nRight >= 0 || nUpper >= 0 || nLower >= 0)
        {
            ::tools::Rectangle aNewBorderRect(nLeft, nUpper, nRight, nLower);
            pPage->ScaleObjects(rNewSize, aNewBorderRect, bScaleAll);

            if (rNewSize.Width() > 0)
                pPage->SetSize(rNewSize);
        }

        if( nLeft  >= 0 || nRight >= 0 || nUpper >= 0 || nLower >= 0 )
        {
            pPage->SetBorder(nLeft, nUpper, nRight, nLower);
        }

        pPage->SetOrientation(eOrientation);
        pPage->SetPaperBin( nPaperBin );
        pPage->SetBackgroundFullSize( bBackgroundFullSize );

        if ( ePageKind == PageKind::Standard )
            GetDoc()->GetMasterSdPage(i, PageKind::Notes)->CreateTitleAndLayout();

        pPage->CreateTitleAndLayout();
    }

    nPageCnt = GetDoc()->GetSdPageCount(ePageKind);

    for (i = 0; i < nPageCnt; i++)
    {
        // then, handle all pages
        pPage = GetDoc()->GetSdPage(i, ePageKind);

        SdUndoAction* pUndo = new SdPageFormatUndoAction(GetDoc(), pPage,
                                pPage->GetSize(),
                                pPage->GetLeftBorder(), pPage->GetRightBorder(),
                                pPage->GetUpperBorder(), pPage->GetLowerBorder(),
                                pPage->GetOrientation(),
                                pPage->GetPaperBin(),
                                pPage->IsBackgroundFullSize(),
                                rNewSize,
                                nLeft, nRight,
                                nUpper, nLower,
                                bScaleAll,
                                eOrientation,
                                nPaperBin,
                                bBackgroundFullSize);
        pUndoGroup->AddAction(pUndo);

        if (rNewSize.Width() > 0 ||
            nLeft  >= 0 || nRight >= 0 || nUpper >= 0 || nLower >= 0)
        {
            ::tools::Rectangle aNewBorderRect(nLeft, nUpper, nRight, nLower);
            pPage->ScaleObjects(rNewSize, aNewBorderRect, bScaleAll);

            if (rNewSize.Width() > 0)
                pPage->SetSize(rNewSize);
        }

        if( nLeft  >= 0 || nRight >= 0 || nUpper >= 0 || nLower >= 0 )
        {
            pPage->SetBorder(nLeft, nUpper, nRight, nLower);
        }

        pPage->SetOrientation(eOrientation);
        pPage->SetPaperBin( nPaperBin );
        pPage->SetBackgroundFullSize( bBackgroundFullSize );

        if ( ePageKind == PageKind::Standard )
        {
            SdPage* pNotesPage = GetDoc()->GetSdPage(i, PageKind::Notes);
            pNotesPage->SetAutoLayout( pNotesPage->GetAutoLayout() );
        }

        pPage->SetAutoLayout( pPage->GetAutoLayout() );
    }
    // use Model-based method at SdDrawDocument
    GetDoc()->AdaptPageSizeForAllPages(
        rNewSize,
        ePageKind,
        pUndoGroup,
        nLeft,
        nRight,
        nUpper,
        nLower,
        bScaleAll,
        eOrientation,
        nPaperBin,
        bBackgroundFullSize);

    // adjust handout page to new format of the standard page
    if( (ePageKind == PageKind::Standard) || (ePageKind == PageKind::Handout) )
    if(0 != nPageCnt && ((ePageKind == PageKind::Standard) || (ePageKind == PageKind::Handout)))
    {
        GetDoc()->GetSdPage(0, PageKind::Handout)->CreateTitleAndLayout(true);
    }

    // handed over undo group to undo manager
    pViewShell->GetViewFrame()->GetObjectShell()
        ->GetUndoManager()->AddUndoAction(pUndoGroup);
    SfxViewShell* pViewShell(GetViewShell());

    long nWidth = pPage->GetSize().Width();
    long nHeight = pPage->GetSize().Height();
    if(nullptr != pViewShell)
    {
        pViewShell->GetViewFrame()->GetObjectShell()->GetUndoManager()->AddUndoAction(pUndoGroup);
    }

    Point aPageOrg(nWidth, nHeight / 2);
    Size aViewSize(nWidth * 3, nHeight * 2);
    // calculate View-Sizes
    SdPage* pPage(0 != nPageCnt
        ? GetDoc()->GetSdPage(0, ePageKind)
        : GetDoc()->GetMasterSdPage(0, ePageKind));
    const long nWidth(pPage->GetSize().Width());
    const long nHeight(pPage->GetSize().Height());
    const Point aPageOrg(nWidth, nHeight / 2);
    const Size aViewSize(nWidth * 3, nHeight * 2);
    Point aVisAreaPos;
    ::sd::View* pView(GetView());
    const Point aNewOrigin(pPage->GetLeftBorder(), pPage->GetUpperBorder());

    InitWindows(aPageOrg, aViewSize, Point(-1, -1), true);

    Point aVisAreaPos;

    if ( GetDocSh()->GetCreateMode() == SfxObjectCreateMode::EMBEDDED )
    {
        aVisAreaPos = GetDocSh()->GetVisArea(ASPECT_CONTENT).TopLeft();
    }

    ::sd::View* pView = GetView();
    if (pView)
    {
        pView->SetWorkArea(::tools::Rectangle(Point() - aVisAreaPos - aPageOrg, aViewSize));
@@ -610,20 +536,19 @@ void ViewShell::SetPageSizeAndBorder(PageKind ePageKind, const Size& rNewSize,

    UpdateScrollBars();

    Point aNewOrigin(pPage->GetLeftBorder(), pPage->GetUpperBorder());

    if (pView)
    {
        pView->GetSdrPageView()->SetPageOrigin(aNewOrigin);
    }

    pViewShell->GetViewFrame()->GetBindings().Invalidate(SID_RULER_NULL_OFFSET);
    if(nullptr != pViewShell)
    {
        pViewShell->GetViewFrame()->GetBindings().Invalidate(SID_RULER_NULL_OFFSET);
        // zoom onto (new) page size
        pViewShell->GetViewFrame()->GetDispatcher()->Execute(SID_SIZE_PAGE, SfxCallMode::ASYNCHRON | SfxCallMode::RECORD);
    }

    // zoom onto (new) page size
    pViewShell->GetViewFrame()->GetDispatcher()->Execute(SID_SIZE_PAGE,
            SfxCallMode::ASYNCHRON | SfxCallMode::RECORD);

    Broadcast (ViewShellHint(ViewShellHint::HINT_PAGE_RESIZE_END));
    Broadcast(ViewShellHint(ViewShellHint::HINT_PAGE_RESIZE_END));
}

/**
diff --git a/svx/source/svdraw/svdmodel.cxx b/svx/source/svdraw/svdmodel.cxx
index 5be0dff..1b17da7 100644
--- a/svx/source/svdraw/svdmodel.cxx
+++ b/svx/source/svdraw/svdmodel.cxx
@@ -1718,6 +1718,18 @@ void SdrModel::setUnoModel( const css::uno::Reference< css::uno::XInterface >& x
    mxUnoModel = xModel;
}

void SdrModel::adaptSizeAndBorderForAllPages(
    const Size& /*rNewSize*/,
    long /*nLeft*/,
    long /*nRight*/,
    long /*nUpper*/,
    long /*nLower*/)
{
    // base implementation does currently nothing. It may be added if needed,
    // but we are on SdrModel level here, thus probably have not enough information
    // to do this for higher-level (derived) Models (e.g. Draw/Impress)
}

uno::Reference< uno::XInterface > SdrModel::createUnoModel()
{
    OSL_FAIL( "SdrModel::createUnoModel() - base implementation should not be called!" );